Skip to content

Commit 317025f

Browse files
authored
Merge pull request #1776 from Vizzuality/bugfix/app/mrxnm-82-split-modal-hidden-values
fix(app): surface every split category in the split modal [MRXNM-82]
2 parents 4deed4a + d516f38 commit 317025f

3 files changed

Lines changed: 100 additions & 48 deletions

File tree

app/e2e/feature-split.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ test.describe('Feature split (materialized)', () => {
5151
// is not exposed as a native combobox/option.
5252
await page.getByText('You can split this feature into categories').click();
5353
await page.getByText(SPLIT_PROPERTY, { exact: true }).click();
54-
await page.locator('.modal-checkbox-list input[type="checkbox"]').first().check();
54+
await page.locator('[data-testid="split-values-list"] input[type="checkbox"]').first().check();
5555

5656
// 4. Save → submits the spec as `created`, triggering BE materialization.
5757
await page.getByRole('button', { name: 'Save' }).click();

app/layout/project/sidebar/scenario/grid-setup/features/modals/split/index.tsx

Lines changed: 80 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ const SplitModal = ({
160160
setSplitFeaturesSelected([]);
161161
}, []);
162162

163+
const onSelectAllSplitValues = useCallback(
164+
(opt: FormValues['splitOption']) => {
165+
setSplitFeaturesSelected(
166+
(getSplitOptionValues(opt) ?? []).map((value) => ({ id: `${value.name}` }))
167+
);
168+
},
169+
[getSplitOptionValues]
170+
);
171+
163172
return (
164173
<FormRFF<FormValues>
165174
initialValues={{
@@ -217,51 +226,80 @@ const SplitModal = ({
217226

218227
<div>
219228
<FieldRFF<string> name="splitValues">
220-
{(fprops) => (
221-
<Field id="splitValues" {...fprops} className="relative">
222-
<div className="flex justify-between">
223-
<div className="modal-checkbox-list max-h-48 w-full space-y-2 overflow-y-auto pt-1">
224-
{getSplitOptionValues(values.splitOption)?.map((value) => {
225-
const checked = !!splitFeaturesSelected.find(
226-
(sfs) => sfs.id === `${value.name}`
227-
);
228-
return (
229-
<div key={value.name} className="flex items-center space-x-2.5 pl-1">
230-
<Checkbox
231-
id={`checkbox-${value.name}`}
232-
value={`${value.name}`}
233-
theme="light"
234-
checked={checked}
235-
className="h-4 w-4"
236-
onChange={onSplitFeaturesChanged}
237-
/>
238-
<label
239-
htmlFor={`checkbox-${value.name}`}
240-
className="ml-2.5 inline-block max-w-sm text-xs"
241-
>
242-
{value.name}
243-
</label>
244-
</div>
245-
);
246-
})}
247-
</div>
229+
{(fprops) => {
230+
const splitOptionValues = getSplitOptionValues(values.splitOption);
231+
232+
return (
233+
<Field id="splitValues" {...fprops} className="relative">
234+
{splitOptionValues && (
235+
<div className="mb-2 flex items-center justify-between">
236+
<p className="text-xs text-gray-400">
237+
{`${splitFeaturesSelected.length} of ${splitOptionValues.length} selected`}
238+
</p>
248239

249-
{getSplitOptionValues(values.splitOption) && (
250-
<div>
251-
<Button
252-
className="flex space-x-2 whitespace-nowrap"
253-
theme="secondary"
254-
size="xs"
255-
onClick={onClearSplitCheckedValues}
256-
>
257-
<p>Clear all</p>
258-
<Icon icon={CLOSE_SVG} className="h-2.5 w-2.5" />
259-
</Button>
240+
<div className="flex space-x-2">
241+
<Button
242+
className="whitespace-nowrap"
243+
theme="secondary"
244+
size="xs"
245+
onClick={() => onSelectAllSplitValues(values.splitOption)}
246+
>
247+
Select all
248+
</Button>
249+
250+
<Button
251+
className="flex space-x-2 whitespace-nowrap"
252+
theme="secondary"
253+
size="xs"
254+
onClick={onClearSplitCheckedValues}
255+
>
256+
<p>Clear all</p>
257+
<Icon icon={CLOSE_SVG} className="h-2.5 w-2.5" />
258+
</Button>
259+
</div>
260260
</div>
261261
)}
262-
</div>
263-
</Field>
264-
)}
262+
263+
{/* `split-values-list` forces a persistently visible scrollbar (see
264+
globals.css): with a categorical attribute the list routinely
265+
overflows, and an auto-hiding scrollbar is the only cue that more
266+
values exist below the fold. */}
267+
<div
268+
className="split-values-list max-h-48 w-full overflow-y-auto"
269+
data-testid="split-values-list"
270+
>
271+
<div className="space-y-2 pr-1 pt-1">
272+
{splitOptionValues?.map((value) => {
273+
const checked = !!splitFeaturesSelected.find(
274+
(sfs) => sfs.id === `${value.name}`
275+
);
276+
return (
277+
<div
278+
key={value.name}
279+
className="flex items-center space-x-2.5 pl-1"
280+
>
281+
<Checkbox
282+
id={`checkbox-${value.name}`}
283+
value={`${value.name}`}
284+
theme="light"
285+
checked={checked}
286+
className="h-4 w-4"
287+
onChange={onSplitFeaturesChanged}
288+
/>
289+
<label
290+
htmlFor={`checkbox-${value.name}`}
291+
className="ml-2.5 inline-block max-w-sm text-xs"
292+
>
293+
{value.name}
294+
</label>
295+
</div>
296+
);
297+
})}
298+
</div>
299+
</div>
300+
</Field>
301+
);
302+
}}
265303
</FieldRFF>
266304
</div>
267305

app/styles/globals.css

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -804,11 +804,25 @@ a.mapboxgl-ctrl-logo.mapboxgl-compact {
804804
}
805805
}
806806

807-
.modal-checkbox-list::-webkit-scrollbar {
808-
display: none;
807+
/* Categorical value lists overflow routinely, and an overlay scrollbar that
808+
auto-hides reads as "nothing more to see". Declaring an explicit width keeps
809+
the scrollbar rendered regardless of the OS "show scroll bars" setting. */
810+
.split-values-list::-webkit-scrollbar {
811+
width: 6px;
812+
}
813+
814+
.split-values-list::-webkit-scrollbar-track {
815+
background: rgba(0, 0, 0, 0.06);
816+
border-radius: 3px;
809817
}
810818

811-
.modal-checkbox-list {
812-
-ms-overflow-style: none; /* IE and Edge */
813-
scrollbar-width: none; /* Firefox */
819+
.split-values-list::-webkit-scrollbar-thumb {
820+
background: rgba(0, 0, 0, 0.28);
821+
border-radius: 3px;
822+
}
823+
824+
.split-values-list {
825+
scrollbar-width: thin;
826+
scrollbar-color: rgba(0, 0, 0, 0.28) rgba(0, 0, 0, 0.06);
814827
}
828+

0 commit comments

Comments
 (0)