✨(react) add the possibility to hide select label
Based on recent feedbacks this feature was needed. It is important for the label to still be accessible to screen readers, that's why we introduced the offscreen class. Resolve #60
This commit is contained in:
@@ -28,4 +28,14 @@
|
||||
padding: 1.5rem 0 0 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&--no-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.labelled-box__children {
|
||||
padding-top: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,43 @@ export default {
|
||||
component: LabelledBox,
|
||||
} as Meta<typeof LabelledBox>;
|
||||
|
||||
const Template: StoryFn<typeof LabelledBox> = (args) => (
|
||||
<div style={{ height: "3.5rem" }}>
|
||||
<LabelledBox {...args} />
|
||||
</div>
|
||||
);
|
||||
const DebugContainer = ({ children }: React.PropsWithChildren<{}>) => {
|
||||
const style: React.CSSProperties = {
|
||||
height: "3.5rem",
|
||||
// A grid background
|
||||
backgroundImage: `linear-gradient(to right, #eee 1px, transparent 1px), linear-gradient(to bottom, #eee 1px, transparent 1px)`,
|
||||
border: "solid #eee",
|
||||
backgroundSize: "1rem 1rem",
|
||||
};
|
||||
return <div style={style}>{children}</div>;
|
||||
};
|
||||
|
||||
const Template: StoryFn<typeof LabelledBox> = (args) => (
|
||||
<DebugContainer>
|
||||
<LabelledBox {...args} />
|
||||
</DebugContainer>
|
||||
);
|
||||
export const Default = {
|
||||
render: Template,
|
||||
|
||||
args: {
|
||||
label: "Your label here",
|
||||
children: <span className="clr-greyscale-800">Hello world</span>,
|
||||
},
|
||||
};
|
||||
|
||||
export const LabelAsPlaceholder = {
|
||||
render: Template,
|
||||
args: {
|
||||
label: "Your label here",
|
||||
labelAsPlaceholder: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const NoLabel = {
|
||||
render: Template,
|
||||
args: {
|
||||
label: "Your label here",
|
||||
hideLabel: true,
|
||||
children: <span className="clr-greyscale-800">Hello world</span>,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { PropsWithChildren } from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
export interface Props extends PropsWithChildren {
|
||||
label?: string;
|
||||
labelAsPlaceholder?: boolean;
|
||||
htmlFor?: string;
|
||||
labelId?: string;
|
||||
hideLabel?: boolean;
|
||||
}
|
||||
|
||||
export const LabelledBox = ({
|
||||
@@ -13,12 +15,20 @@ export const LabelledBox = ({
|
||||
labelAsPlaceholder,
|
||||
htmlFor,
|
||||
labelId,
|
||||
hideLabel,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className="labelled-box">
|
||||
<div
|
||||
className={classNames("labelled-box", {
|
||||
"labelled-box--no-label": hideLabel,
|
||||
})}
|
||||
>
|
||||
{label && (
|
||||
<label
|
||||
className={labelAsPlaceholder ? "placeholder" : ""}
|
||||
className={classNames("labelled-box__label", {
|
||||
placeholder: labelAsPlaceholder,
|
||||
offscreen: hideLabel,
|
||||
})}
|
||||
htmlFor={htmlFor}
|
||||
id={labelId}
|
||||
>
|
||||
|
||||
@@ -93,6 +93,16 @@ You can disable some options by using the `disabled` props on the `Option` objec
|
||||
<Story id="components-forms-select-mono--disabled-options"/>
|
||||
</Canvas>
|
||||
|
||||
## Hide label
|
||||
|
||||
For some reasons you might want to hide the label of the select. You can do that by using the `hideLabel` props.
|
||||
|
||||
> It is important for accessibility to always have a label for your inputs. Keep in mind that setting `hideLabel` to `true`, will only hide the label visually, but it will still be available for screen readers in the DOM.
|
||||
|
||||
<Canvas sourceState="shown">
|
||||
<Story id="components-forms-select-mono--hidden-label"/>
|
||||
</Canvas>
|
||||
|
||||
## Controlled / Non Controlled
|
||||
|
||||
Like a native select, you can use the Select component in a controlled or non controlled way. You can see the example below
|
||||
|
||||
@@ -86,6 +86,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.labelled-box--no-label {
|
||||
.c__select__inner {
|
||||
align-items: center;
|
||||
&__actions {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__menu {
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
|
||||
@@ -1133,5 +1133,37 @@ describe("<Select/>", () => {
|
||||
expectMenuToBeClosed(menu);
|
||||
expect(valueRendered).toHaveTextContent("Tokyo");
|
||||
});
|
||||
|
||||
it("is accessible if the the label is not shown", async () => {
|
||||
render(
|
||||
<CunninghamProvider>
|
||||
<Select
|
||||
label="City"
|
||||
options={[
|
||||
{
|
||||
label: "Paris",
|
||||
},
|
||||
{
|
||||
label: "London",
|
||||
},
|
||||
{
|
||||
label: "New York",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: "Tokyo",
|
||||
},
|
||||
]}
|
||||
hideLabel={true}
|
||||
/>
|
||||
</CunninghamProvider>
|
||||
);
|
||||
// Make sure the input is accessible.
|
||||
screen.getByRole("combobox", {
|
||||
name: "City",
|
||||
});
|
||||
const label = screen.getByText("City");
|
||||
expect(Array.from(label.classList)).toContain("offscreen");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ interface Option {
|
||||
type Props = PropsWithChildren &
|
||||
FieldProps & {
|
||||
label: string;
|
||||
hideLabel?: boolean;
|
||||
options: Option[];
|
||||
searchable?: boolean;
|
||||
name?: string;
|
||||
@@ -93,6 +94,7 @@ const SelectAux = ({
|
||||
options,
|
||||
name,
|
||||
label,
|
||||
hideLabel,
|
||||
labelAsPlaceholder,
|
||||
downshiftProps,
|
||||
downshiftReturn,
|
||||
@@ -142,8 +144,10 @@ const SelectAux = ({
|
||||
value={optionToValue(downshiftReturn.selectedItem)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<LabelledBox
|
||||
label={label}
|
||||
hideLabel={hideLabel}
|
||||
labelAsPlaceholder={labelAsPlaceholder}
|
||||
htmlFor={labelProps.htmlFor}
|
||||
labelId={labelProps.id}
|
||||
|
||||
@@ -56,6 +56,17 @@ export const WithText = {
|
||||
},
|
||||
};
|
||||
|
||||
export const HiddenLabel = {
|
||||
render: Template,
|
||||
|
||||
args: {
|
||||
label: "Select a city",
|
||||
hideLabel: true,
|
||||
options: OPTIONS,
|
||||
defaultValue: OPTIONS[4].value,
|
||||
},
|
||||
};
|
||||
|
||||
export const Controlled = () => {
|
||||
const [value, setValue] = useState(OPTIONS[8].value);
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user