Don't fallback to name on label to help clarify the different uses for each and allow label-less filters.

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2022-01-12 10:59:51 +01:00
parent 21705f6432
commit 1dbe63ec39
7 changed files with 74 additions and 24 deletions
+10
View File
@@ -0,0 +1,10 @@
---
'@backstage/plugin-search': minor
---
The way labels are controlled on both the `<SearchFilter.Checkbox />` and
`<SearchFilter.Select />` components has changed. Previously, the string passed
on the `name` prop (which controls the field being filtered on) was also
rendered as the field label. Now, if you want a label rendered, it must be
passed on the new `label` prop. If no `label` is provided, no label will be
rendered.
+27
View File
@@ -0,0 +1,27 @@
---
'@backstage/create-app': patch
---
A `label` prop was added to `<SearchFilter.* />` components in order to allow
user-friendly label strings (as well as the option to omit a label). In order
to maintain labels on your existing filters, add a `label` prop to them in your
`SearchPage.tsx`.
```diff
--- a/packages/app/src/components/search/SearchPage.tsx
+++ b/packages/app/src/components/search/SearchPage.tsx
@@ -96,11 +96,13 @@ const SearchPage = () => {
)}
<SearchFilter.Select
className={classes.filter}
+ label="Kind"
name="kind"
values={['Component', 'Template']}
/>
<SearchFilter.Checkbox
className={classes.filter}
+ label="Lifecycle"
name="lifecycle"
values={['experimental', 'production']}
/>
```
@@ -115,11 +115,13 @@ const SearchPage = () => {
)}
<SearchFilter.Select
className={classes.filter}
label="Kind"
name="kind"
values={['Component', 'Template']}
/>
<SearchFilter.Checkbox
className={classes.filter}
label="Lifecycle"
name="lifecycle"
values={['experimental', 'production']}
/>
@@ -96,11 +96,13 @@ const SearchPage = () => {
)}
<SearchFilter.Select
className={classes.filter}
label="Kind"
name="kind"
values={['Component', 'Template']}
/>
<SearchFilter.Checkbox
className={classes.filter}
label="Lifecycle"
name="lifecycle"
values={['experimental', 'production']}
/>
@@ -78,7 +78,7 @@ export const AutocompleteFilter = (props: SearchAutocompleteFilterProps) => {
{...params}
name="search"
variant="outlined"
label={label || name}
label={label}
fullWidth
/>
);
@@ -34,6 +34,7 @@ describe('SearchFilter', () => {
types: [],
};
const label = 'Field';
const name = 'field';
const values = ['value1', 'value2'];
const filters = { unrelated: 'unrelated' };
@@ -57,12 +58,12 @@ describe('SearchFilter', () => {
it('Renders field name and values when provided as props', async () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchFilter.Checkbox name={name} values={values} />
<SearchFilter.Checkbox label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
expect(
@@ -83,12 +84,12 @@ describe('SearchFilter', () => {
},
}}
>
<SearchFilter.Checkbox name={name} values={values} />
<SearchFilter.Checkbox label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
expect(
@@ -101,6 +102,7 @@ describe('SearchFilter', () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchFilter.Checkbox
label={label}
name={name}
values={values}
defaultValue={[values[0]]}
@@ -109,7 +111,7 @@ describe('SearchFilter', () => {
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
expect(screen.getByRole('checkbox', { name: values[0] })).toBeChecked();
@@ -121,12 +123,12 @@ describe('SearchFilter', () => {
it('Checking / unchecking a value sets filter state', async () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchFilter.Checkbox name={name} values={values} />
<SearchFilter.Checkbox label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
const checkBox = screen.getByRole('checkbox', { name: values[0] });
@@ -151,12 +153,12 @@ describe('SearchFilter', () => {
it('Checking / unchecking a value maintains unrelated filter state', async () => {
render(
<SearchContextProvider initialState={{ ...initialState, filters }}>
<SearchFilter.Checkbox name={name} values={values} />
<SearchFilter.Checkbox label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
const checkBox = screen.getByRole('checkbox', { name: values[0] });
@@ -185,12 +187,12 @@ describe('SearchFilter', () => {
it('Renders field name and values when provided as props', async () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchFilter.Select name={name} values={values} />
<SearchFilter.Select label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
userEvent.click(screen.getByRole('button'));
@@ -210,7 +212,11 @@ describe('SearchFilter', () => {
it('Renders values when provided asynchronously', async () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchFilter.Select name={name} asyncValues={async () => values} />
<SearchFilter.Select
label={label}
name={name}
asyncValues={async () => values}
/>
</SearchContextProvider>,
);
@@ -245,12 +251,12 @@ describe('SearchFilter', () => {
},
}}
>
<SearchFilter.Select name={name} values={values} />
<SearchFilter.Select label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
userEvent.click(screen.getByRole('button'));
@@ -276,6 +282,7 @@ describe('SearchFilter', () => {
<SearchContextProvider initialState={initialState}>
<SearchFilter.Select
name={name}
label={label}
values={values}
defaultValue={values[0]}
/>
@@ -283,7 +290,7 @@ describe('SearchFilter', () => {
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
userEvent.click(screen.getByRole('button'));
@@ -307,12 +314,12 @@ describe('SearchFilter', () => {
it('Selecting a value sets filter state', async () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchFilter.Select name={name} values={values} />
<SearchFilter.Select label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
const button = screen.getByRole('button');
@@ -358,12 +365,12 @@ describe('SearchFilter', () => {
filters,
}}
>
<SearchFilter.Select name={name} values={values} />
<SearchFilter.Select label={label} name={name} values={values} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByText(name)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
});
const button = screen.getByRole('button');
@@ -93,7 +93,7 @@ const CheckboxFilter = (props: SearchFilterComponentProps) => {
fullWidth
data-testid="search-checkboxfilter-next"
>
<FormLabel className={classes.label}>{label || name}</FormLabel>
{label ? <FormLabel className={classes.label}>{label}</FormLabel> : null}
{values.map((value: string) => (
<FormControlLabel
key={value}
@@ -154,9 +154,11 @@ const SelectFilter = (props: SearchFilterComponentProps) => {
fullWidth
data-testid="search-selectfilter-next"
>
<InputLabel className={classes.label} margin="dense">
{label || name}
</InputLabel>
{label ? (
<InputLabel className={classes.label} margin="dense">
{label}
</InputLabel>
) : null}
<Select
variant="outlined"
value={filters[name] || ''}