Skip to content

Commit

Permalink
feat(FocusRing): mvp for FocusRing component (#37)
Browse files Browse the repository at this point in the history
* feat(FocusRing): create component WIP

* feat(FocusRing): creating ring logic, WIP

* feat(FocusRing): mvp of focusRing component

* fix(FocusRing): comment clean up

* feat(FocusRing): updated docs and cleaned up code

* feat(FocusRing): cleaned up docs

* feat(FocusRing): updated exports

* chore(FocusRing): clean up comments

* feat(FocusRing): clean up docs update ref type
  • Loading branch information
ctoddy authored Aug 21, 2024
1 parent 8d9a739 commit 7ce2630
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 0 deletions.
102 changes: 102 additions & 0 deletions packages/solid/components/FocusRing/FocusRing.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2024 Comcast Cable Communications Management, LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
import type { Meta, StoryObj } from 'storybook-solidjs';
import FocusRing from './FocusRing.jsx';

type Story = StoryObj<typeof FocusRing>;

/**
* Fancy ring to focus on Items.
*
* ## Usage
* These steps set up a FocusRing component and associates it with a reference. This reference is then used by the setFocusRing function and the focus manager to set the focus ring to the current activeElement.
* #### 1. Necessary imports:
*
* - `useFocusManager`: an Effect used to manage focus within the application. See more on `useFocusManager` [here](https://lightning-tv.github.io/solid/#/primitives/useFocusManager?id=usefocusmanager-for-key-handling).
* - `onMount`: This lifecyle is used to run code after the component has mounted.
* - `setFocusRing`: This function in the FocusRing package is used to set the FocusRing reference and position the component on the current `activeElement`.
* - `FocusRing` : The FocusRing itself.
*
* ```js
* import { onMount } from 'solid-js';
* import { useFocusManager } from '@lightningtv/solid/primitives';
* import { FocusRing, setFocusRing } from '@lightningtv/solid-ui';
* ```
* #### 2. Create a ref:
* Declare an empty variable that will be used to store a reference to the `FocusRing` component. For example `let focusRef;`
*
* #### 3. Use the focus manager:
* Use the focus manager (`useFocusManager();`) Effect to activate focus management.
*
* #### 4. Use the FocusRing reference after the components are rendered:
* This code runs after the component has mounted. It calls the `setFocusRing` function to position the FocusRing to the current `activeElement` using the ref variable.
*
* ```js
* onMount(() => {
* setFocusRing(focusRef);
* });
* ```
* #### 5. Render the FocusRing component and use the declared reference variable:
* This line renders the `FocusRing` component, passing `focusRef` as a prop. This allows the `setFocusRing` function to associate the FocusRing with the component instance.
*
* ```js
* <FocusRing ref={focusRef} />
* ```
* ## Example usage
*
* ```js
* import { View } from '@lightningtv/solid';
* import { onMount } from 'solid-js';
* import { useFocusManager } from '@lightningtv/solid/primitives';
* import { Button, FocusRing, setFocusRing } from '@lightningtv/solid-ui';
*
* const App = () => {
* let focusRef; // Declared reference variables
* useFocusManager(); // Effect that manages focus
*
* onMount(() => { // Function that runs after components are mounted
* setFocusRing(focusRef);// Sets rings location to the current focused element
* });
*
* return (
* <View>
* <FocusRing ref={focusRef} /> // Passing the ref variable as a ref prop
* // Other components that receive focus
* <Button autofocus width={100}> 1 </Button>
* <Button width={100}> 2 </Button>
* </View>
* );
* };
*
* export default App;
* ```
*/

const meta: Meta<typeof FocusRing> = {
title: 'Components/FocusRing',
component: FocusRing,
tags: ['autodocs']
};

export default meta;

export const Basic: Story = {
render: args => {
return <FocusRing {...args} width={350} height={200} />;
},
args: {}
};
48 changes: 48 additions & 0 deletions packages/solid/components/FocusRing/FocusRing.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2024 Comcast Cable Communications Management, LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import theme from 'theme';
import { makeComponentStyles } from '../../utils/index.js';
import type { FocusRingStyles, FocusRingConfig } from './FocusRing.types.js';

/* @ts-expect-error next-line themes are supplied by client applications so this setup is necessary */
const { FocusRing: { defaultTone, ...themeStyles } = { themeStyles: {} } } = theme?.componentConfig;

const container: FocusRingConfig = {
themeKeys: {
borderRadius: 'radius'
},
base: {
x: 0,
y: 0,
borderRadius: theme.radius.lg,
border: {
color: theme.color.interactiveNeutralFocus,
width: theme.stroke.md
}
},
themeStyles
};

const Container = makeComponentStyles<FocusRingStyles['Container']>(container);

const styles: FocusRingStyles = {
tone: defaultTone || 'neutral',
Container
};

export default styles;
38 changes: 38 additions & 0 deletions packages/solid/components/FocusRing/FocusRing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2024 Comcast Cable Communications Management, LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import { View } from '@lightningtv/solid';
import { type Component } from 'solid-js';
import type { FocusRingProps } from './FocusRing.types.js';
import styles from './FocusRing.styles.js';

const FocusRing: Component<FocusRingProps> = (props: FocusRingProps) => {
return (
<View
{...props}
// @ts-expect-error TODO type needs to be fixed in framework
style={[
props.style, //
styles.Container.tones[props.tone ?? styles.tone],
styles.Container.base
]}
forwardStates
/>
);
};

export default FocusRing;
40 changes: 40 additions & 0 deletions packages/solid/components/FocusRing/FocusRing.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2024 Comcast Cable Communications Management, LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import type { UIComponentProps } from 'types/interfaces.js';
import type { ComponentStyleConfig, NodeStyleSet, Tone } from 'types/types.js';
import type { NodeStyles } from '@lightningtv/solid';

export interface FocusRingProps extends UIComponentProps {
/**
* placeholder for FocusRingProps
*/
}

export interface FocusRingStyleProperties {
/**
* corner radius of input
*/
radius?: NodeStyles['borderRadius'];
}

export interface FocusRingStyles {
tone: Tone;
Container: NodeStyleSet;
}

export type FocusRingConfig = ComponentStyleConfig<FocusRingStyleProperties>;
21 changes: 21 additions & 0 deletions packages/solid/components/FocusRing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2024 Comcast Cable Communications Management, LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

export { default as default } from './FocusRing.jsx';
export { default as focusRingStyles } from './FocusRing.styles.js';
export type { FocusRingProps, FocusRingStyles } from './FocusRing.types.js';
export { setFocusRing } from './setFocusRing.js';
34 changes: 34 additions & 0 deletions packages/solid/components/FocusRing/setFocusRing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2024 Comcast Cable Communications Management, LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import { createEffect, on } from 'solid-js';
import { activeElement, type SolidNode } from '@lightningtv/solid';

export const setFocusRing = (focusRingRef: SolidNode) =>
createEffect(
on(
activeElement,
element => {
focusRingRef.parent = element.parent;
focusRingRef.x = element.x;
focusRingRef.y = element.y;
focusRingRef.width = element.width;
focusRingRef.height = element.height;
},
{ defer: true }
)
);
7 changes: 7 additions & 0 deletions packages/solid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ export {
type ColumnProps,
type ColumnStyles
} from './components/Column/index.js';
export {
default as FocusRing,
focusRingStyles,
type FocusRingProps,
type FocusRingStyles,
setFocusRing
} from './components/FocusRing/index.js';
export { default as Icon, iconStyles, type IconProps, type IconStyles } from './components/Icon/index.js';
export {
default as Input,
Expand Down

0 comments on commit 7ce2630

Please sign in to comment.