Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structure and slots for SearchBox, using Input as a slot #28090

Merged
merged 36 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4b9205e
add slots and types for SearchBox
emmayjiang May 31, 2023
1182bf3
fixing react-input version
emmayjiang May 31, 2023
790194f
updating yarn.lock
emmayjiang Jun 1, 2023
3ba7f4d
reorganizing code + alphabetizing package.json
emmayjiang Jun 1, 2023
9dfa1e6
add slots and types for SearchBox
emmayjiang May 31, 2023
536871b
fixing react-input version
emmayjiang May 31, 2023
208d14a
updating yarn.lock
emmayjiang Jun 1, 2023
a2132e7
reorganizing code + alphabetizing package.json
emmayjiang Jun 1, 2023
1abcf33
fix type errors
emmayjiang Jun 1, 2023
9e04463
fix merge conflicts
emmayjiang Jun 1, 2023
7c46d96
rebuild
emmayjiang Jun 5, 2023
1d7698f
styles
emmayjiang Jun 7, 2023
b01fb29
remove pseudoelements from input
emmayjiang Jun 8, 2023
3f46b1f
dismiss button functionality
emmayjiang Jun 8, 2023
d002d4d
packages
emmayjiang Jun 8, 2023
0b33d33
fixes to dismiss button
emmayjiang Jun 8, 2023
c11417b
code revisions following sarah's feedback
emmayjiang Jun 8, 2023
12f9886
styling issues
emmayjiang Jun 8, 2023
881eff8
remove contentBefore slot
emmayjiang Jun 8, 2023
c09bc34
type fixes & handling custom dismiss callback
emmayjiang Jun 9, 2023
fb5fd63
api update
emmayjiang Jun 9, 2023
96824e7
Merge branch 'master' into react-search-create-SearchBox-slots
emmayjiang Jun 12, 2023
525e629
minor nits
emmayjiang Jun 12, 2023
5dac8e5
refactor 'disabled'
emmayjiang Jun 12, 2023
5008246
api update
emmayjiang Jun 12, 2023
024eb53
Update packages/react-components/react-search/src/components/SearchBo…
emmayjiang Jun 12, 2023
713dd7a
api update
emmayjiang Jun 12, 2023
94e109d
api update
emmayjiang Jun 12, 2023
0b654c3
Merge branch 'master' into react-search-create-SearchBox-slots
emmayjiang Jun 13, 2023
8aa06b5
fixing styles errors
emmayjiang Jun 14, 2023
3d66691
update react-icons version
emmayjiang Jun 15, 2023
6a51ee8
fixes to ref + default
emmayjiang Jun 15, 2023
1aace49
fix to ref
emmayjiang Jun 15, 2023
d571f62
api update
emmayjiang Jun 15, 2023
c04b6d7
fix tests
emmayjiang Jun 15, 2023
e9e42db
api update
emmayjiang Jun 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions packages/react-components/react-search/etc/react-search.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

```ts

/// <reference types="react" />

import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import { Input } from '@fluentui/react-input';
import { InputState } from '@fluentui/react-input';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';
Expand All @@ -21,18 +25,20 @@ export const SearchBox: ForwardRefComponent<SearchBoxProps>;
export const searchBoxClassNames: SlotClassNames<SearchBoxSlots>;

// @public
export type SearchBoxProps = ComponentProps<SearchBoxSlots> & {};
export type SearchBoxProps = ComponentProps<SearchBoxSlots>;

// @public (undocumented)
export type SearchBoxSlots = {
root: Slot<'div'>;
root: NonNullable<Slot<typeof Input>>;
dismiss?: Slot<'span'>;
contentAfter?: Slot<'span'>;
};

// @public
export type SearchBoxState = ComponentState<SearchBoxSlots>;
export type SearchBoxState = ComponentState<SearchBoxSlots> & Required<Pick<InputState, 'size'>> & Required<Pick<SearchBoxProps, 'disabled'>>;

// @public
export const useSearchBox_unstable: (props: SearchBoxProps, ref: React_2.Ref<HTMLElement>) => SearchBoxState;
export const useSearchBox_unstable: (props: SearchBoxProps, ref: React_2.Ref<HTMLInputElement>) => SearchBoxState;

// @public
export const useSearchBoxStyles_unstable: (state: SearchBoxState) => SearchBoxState;
Expand Down
2 changes: 2 additions & 0 deletions packages/react-components/react-search/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@fluentui/scripts-tasks": "*"
},
"dependencies": {
"@fluentui/react-icons": "^2.0.203",
"@fluentui/react-input": "^9.4.16",
"@fluentui/react-jsx-runtime": "9.0.0-alpha.6",
"@fluentui/react-theme": "^9.1.8",
"@fluentui/react-utilities": "^9.9.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ describe('SearchBox', () => {
isConformant({
Component: SearchBox,
displayName: 'SearchBox',
primarySlot: 'input',
});

// TODO add more tests here, and create visual regression tests in /apps/vr-tests

it('renders a default state', () => {
const result = render(<SearchBox>Default SearchBox</SearchBox>);
const result = render(<SearchBox />);
expect(result.container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
import { Input, InputState } from '@fluentui/react-input';

export type SearchBoxSlots = {
root: Slot<'div'>;
// Root of the component, wrapping the inputs
root: NonNullable<Slot<typeof Input>>;

// Last element in the input, within the input border
dismiss?: Slot<'span'>;

// Element after the input text, within the input border
contentAfter?: Slot<'span'>;
};

/**
* SearchBox Props
*/
export type SearchBoxProps = ComponentProps<SearchBoxSlots> & {};
export type SearchBoxProps = ComponentProps<SearchBoxSlots>;

/**
* State used in rendering SearchBox
*/
export type SearchBoxState = ComponentState<SearchBoxSlots>;
// TODO: Remove semicolon from previous line, uncomment next line, and provide union of props to pick from SearchBoxProps.
// & Required<Pick<SearchBoxProps, 'propName'>>
export type SearchBoxState = ComponentState<SearchBoxSlots> &
Required<Pick<InputState, 'size'>> &
Required<Pick<SearchBoxProps, 'disabled'>>;
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,56 @@

exports[`SearchBox renders a default state 1`] = `
<div>
<div
class="fui-SearchBox"
<span
class="fui-Input fui-SearchBox"
>
Default SearchBox
</div>
<span
class="fui-Input__contentBefore"
>
<svg
aria-hidden="true"
class=""
fill="currentColor"
height="1em"
viewBox="0 0 20 20"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.5 3a5.5 5.5 0 0 1 4.23 9.02l4.12 4.13a.5.5 0 0 1-.63.76l-.07-.06-4.13-4.12A5.5 5.5 0 1 1 8.5 3Zm0 1a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9Z"
fill="currentColor"
/>
</svg>
</span>
<input
class="fui-Input__input"
type="search"
value=""
/>
<span
class="fui-Input__contentAfter fui-SearchBox__contentAfter"
>
<span
aria-label="clear"
class="fui-SearchBox__dismiss"
role="button"
>
<svg
aria-hidden="true"
class=""
fill="currentColor"
height="1em"
viewBox="0 0 20 20"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m4.09 4.22.06-.07a.5.5 0 0 1 .63-.06l.07.06L10 9.29l5.15-5.14a.5.5 0 0 1 .63-.06l.07.06c.18.17.2.44.06.63l-.06.07L10.71 10l5.14 5.15c.18.17.2.44.06.63l-.06.07a.5.5 0 0 1-.63.06l-.07-.06L10 10.71l-5.15 5.14a.5.5 0 0 1-.63.06l-.07-.06a.5.5 0 0 1-.06-.63l.06-.07L9.29 10 4.15 4.85a.5.5 0 0 1-.06-.63l.06-.07-.06.07Z"
fill="currentColor"
/>
</svg>
</span>
</span>
</span>
</div>
`;
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/** @jsxRuntime classic */
/** @jsx createElement */
/** @jsxFrag React.Fragment */

import * as React from 'react';
import { createElement } from '@fluentui/react-jsx-runtime';
import { getSlotsNext } from '@fluentui/react-utilities';
import type { SearchBoxState, SearchBoxSlots } from './SearchBox.types';
Expand All @@ -12,5 +14,17 @@ export const renderSearchBox_unstable = (state: SearchBoxState) => {
const { slots, slotProps } = getSlotsNext<SearchBoxSlots>(state);

// TODO Add additional slots in the appropriate place
return <slots.root {...slotProps.root} />;
const rootSlots = {
contentAfter: slots.contentAfter && {
...slotProps.contentAfter,
children: (
<>
{slotProps.contentAfter.children}
{slots.dismiss && <slots.dismiss {...slotProps.dismiss} />}
</>
),
},
};

return <slots.root {...slotProps.root} {...rootSlots} />;
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as React from 'react';
import { mergeCallbacks, resolveShorthand, useControllableState, useEventCallback } from '@fluentui/react-utilities';
import { Input } from '@fluentui/react-input';
import type { SearchBoxProps, SearchBoxState } from './SearchBox.types';
import { DismissRegular, SearchRegular } from '@fluentui/react-icons';

/**
* Create the state required to render SearchBox.
*
* The returned state can be modified with hooks such as useSearchBoxStyles_unstable,
* before being passed to renderSearchBox_unstable.
*
* @param props - props from this instance of SearchBox
* @param ref - reference to root HTMLElement of SearchBox
*/
export const useSearchBox_unstable = (props: SearchBoxProps, ref: React.Ref<HTMLInputElement>): SearchBoxState => {
const { size = 'medium', disabled = false, contentBefore, dismiss, contentAfter, ...inputProps } = props;

const [value, setValue] = useControllableState({
state: props.value,
defaultState: props.defaultValue,
initialState: '',
});

const state: SearchBoxState = {
components: {
root: Input,
dismiss: 'span',
contentAfter: 'span',
},

root: {
ref,
type: 'search',
input: {}, // defining here to have access in styles hook
value,

contentBefore: resolveShorthand(contentBefore, {
defaultProps: {
children: <SearchRegular />,
},
required: true, // TODO need to allow users to remove
}),

...inputProps,

onChange: useEventCallback(ev => {
const newValue = ev.target.value;
props.onChange?.(ev, { value: newValue });
setValue(newValue);
}),
},
dismiss: resolveShorthand(dismiss, {
defaultProps: {
children: <DismissRegular />,
role: 'button',
'aria-label': 'clear',
},
required: true,
}),
contentAfter: resolveShorthand(contentAfter, { required: true }),

disabled,
size,
};

const onDismissClick = useEventCallback(mergeCallbacks(state.dismiss?.onClick, () => setValue('')));
if (state.dismiss) {
state.dismiss.onClick = onDismissClick;
}

return state;
};
Loading