|
1 | 1 | /* eslint-disable perfectionist/sort-objects */ |
2 | 2 |
|
3 | | -import { useEffect, useState } from 'react'; |
| 3 | +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; |
4 | 4 |
|
5 | | -import { sleep } from '@douglasneuroinformatics/libjs'; |
| 5 | +import { filterObject, sleep } from '@douglasneuroinformatics/libjs'; |
6 | 6 | import type { ZodTypeLike } from '@douglasneuroinformatics/libjs'; |
7 | 7 | import type { FormFields } from '@douglasneuroinformatics/libui-form-types'; |
8 | 8 | import type FormTypes from '@douglasneuroinformatics/libui-form-types'; |
@@ -535,6 +535,86 @@ export const WithSuspend: StoryObj<typeof Form<ZodTypeLike<FormTypes.Data>, { de |
535 | 535 | } |
536 | 536 | }; |
537 | 537 |
|
| 538 | +export const WithSubscribe: StoryObj< |
| 539 | + typeof Form<ZodTypeLike<FormTypes.Data>, { options: string; searchTerm: string }> |
| 540 | +> = { |
| 541 | + decorators: [ |
| 542 | + (Story) => { |
| 543 | + const defaultOptions = useMemo<{ [key: string]: string }>( |
| 544 | + () => ({ |
| 545 | + a: 'Option A', |
| 546 | + b: 'Option B', |
| 547 | + c: 'Option C', |
| 548 | + d: 'Option D' |
| 549 | + }), |
| 550 | + [] |
| 551 | + ); |
| 552 | + const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined); |
| 553 | + const [options, setOptions] = useState<typeof defaultOptions>(defaultOptions); |
| 554 | + |
| 555 | + const handleChange = useCallback( |
| 556 | + ( |
| 557 | + values: Partial<{ options: string; searchTerm: string }>, |
| 558 | + setValues: React.Dispatch<React.SetStateAction<Partial<{ options: string; searchTerm: string }>>> |
| 559 | + ) => { |
| 560 | + clearTimeout(timeoutRef.current); |
| 561 | + const lowerCaseSearch = values.searchTerm?.toLowerCase(); |
| 562 | + if (!lowerCaseSearch) { |
| 563 | + setOptions(defaultOptions); |
| 564 | + return; |
| 565 | + } |
| 566 | + timeoutRef.current = setTimeout(() => { |
| 567 | + const updatedOptions = filterObject(defaultOptions, (value) => |
| 568 | + value.toLowerCase().includes(lowerCaseSearch) |
| 569 | + ) as { |
| 570 | + [key: string]: string; |
| 571 | + }; |
| 572 | + setOptions(updatedOptions); |
| 573 | + if (values.options !== undefined && !Object.keys(updatedOptions).includes(values.options)) { |
| 574 | + setValues((prevValues) => ({ |
| 575 | + ...prevValues, |
| 576 | + options: undefined |
| 577 | + })); |
| 578 | + } |
| 579 | + }, 500); |
| 580 | + }, |
| 581 | + [] |
| 582 | + ); |
| 583 | + |
| 584 | + return ( |
| 585 | + <Story |
| 586 | + args={{ |
| 587 | + content: { |
| 588 | + searchTerm: { |
| 589 | + kind: 'string', |
| 590 | + label: 'Search Term', |
| 591 | + variant: 'input' |
| 592 | + }, |
| 593 | + options: { |
| 594 | + kind: 'string', |
| 595 | + label: 'Options', |
| 596 | + options, |
| 597 | + variant: 'select' |
| 598 | + } |
| 599 | + }, |
| 600 | + subscribe: { |
| 601 | + onChange: handleChange, |
| 602 | + selector: (values) => values.searchTerm |
| 603 | + }, |
| 604 | + onSubmit: (data) => { |
| 605 | + alert(JSON.stringify(data, (_key, value) => (value instanceof Set ? [...value] : (value as unknown)), 2)); |
| 606 | + }, |
| 607 | + validationSchema: z.object({ |
| 608 | + searchTerm: z.string().min(1), |
| 609 | + options: z.enum(['a', 'b', 'c', 'd']) |
| 610 | + }) |
| 611 | + }} |
| 612 | + /> |
| 613 | + ); |
| 614 | + } |
| 615 | + ] |
| 616 | +}; |
| 617 | + |
538 | 618 | export const WithError: StoryObj<typeof Form> = { |
539 | 619 | args: { |
540 | 620 | content: { |
|
0 commit comments