Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 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
15 changes: 7 additions & 8 deletions packages/react-components/react-search/etc/react-search.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@

```ts

import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
/// <reference types="react" />

import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';

// @public
export const renderSearchBox_unstable: (state: SearchBoxState) => JSX.Element;
Expand All @@ -21,15 +18,17 @@ 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> & Pick<InputState, 'size'> & Required<Pick<SearchBoxProps, 'disabled'>>;

// @public
export const useSearchBox_unstable: (props: SearchBoxProps, ref: React_2.Ref<HTMLElement>) => 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.196",
"@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
@@ -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> &
Pick<InputState, 'size'> &
Required<Pick<SearchBoxProps, 'disabled'>>;
Comment thread
emmayjiang marked this conversation as resolved.
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,72 @@
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<HTMLElement>): 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: {
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
Comment thread
emmayjiang marked this conversation as resolved.
}),

...inputProps,

onChange: useEventCallback(ev => {
const newValue = ev.target.value;
props.onChange?.(ev, { value: newValue });
setValue(newValue);
}),
Comment thread
emmayjiang marked this conversation as resolved.
},
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;
};
Original file line number Diff line number Diff line change
@@ -1,33 +1,72 @@
import { makeStyles, mergeClasses } from '@griffel/react';
import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react';
import type { SearchBoxSlots, SearchBoxState } from './SearchBox.types';
import type { SlotClassNames } from '@fluentui/react-utilities';
import { tokens } from '@fluentui/react-theme';

export const searchBoxClassNames: SlotClassNames<SearchBoxSlots> = {
root: 'fui-SearchBox',
// TODO: add class names for all slots on SearchBoxSlots.
// Should be of the form `<slotName>: 'fui-SearchBox__<slotName>`
dismiss: 'fui-SearchBox__dismiss',
contentAfter: 'fui-SearchBox__contentAfter',
};

/**
* Styles for the root slot
*/
const useStyles = makeStyles({
root: {
// TODO Add default styles for the root element
const useRootClassName = makeResetStyles({
// removes the WebKit pseudoelement styling
'::-webkit-search-decoration': {
display: 'none',
},
'::-webkit-search-cancel-button': {
display: 'none',
},
});

// TODO add additional classes for different states and/or slots
const useDismissClassName = makeResetStyles({
boxSizing: 'border-box',
color: tokens.colorNeutralForeground3, // "icon color" in design spec
display: 'flex',
// special case styling for icons (most common case) to ensure they're centered vertically
// size: medium (default)
'> svg': { fontSize: '20px' },
});
Comment thread
emmayjiang marked this conversation as resolved.

const useDismissStyles = makeStyles({
disabled: {
color: tokens.colorNeutralForegroundDisabled,
},
// Ensure resizable icons show up with the proper font size
small: {
'> svg': { fontSize: '16px' },
},
medium: {
// included in useDismissClassName
},
large: {
'> svg': { fontSize: '24px' },
},
});

/**
* Apply styling to the SearchBox slots based on the state
*/
export const useSearchBoxStyles_unstable = (state: SearchBoxState): SearchBoxState => {
const styles = useStyles();
state.root.className = mergeClasses(searchBoxClassNames.root, styles.root, state.root.className);
const { size, disabled } = state;

const DismissClassName = useDismissClassName();
const DismissStyles = useDismissStyles();

state.root.input!.className = mergeClasses(useRootClassName(), state.root.className);

// TODO Add class names to slots, for example:
// state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className);
if (state.dismiss) {
state.dismiss.className = mergeClasses(
searchBoxClassNames.dismiss,
DismissClassName,
disabled && DismissStyles.disabled,
DismissStyles[size],
state.dismiss.className,
);
}

return state;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as React from 'react';
import { SearchBox, SearchBoxProps } from '@fluentui/react-search';

export const Default = (props: Partial<SearchBoxProps>) => <SearchBox {...props} />;
import { FilterRegular } from '@fluentui/react-icons';

export const Default = (props: Partial<SearchBoxProps>) => <SearchBox {...props} contentAfter={<FilterRegular />} />;
Comment thread
emmayjiang marked this conversation as resolved.