Skip to content

Commit

Permalink
Redesign Inputs API (#1619)
Browse files Browse the repository at this point in the history
  • Loading branch information
ovidiuch committed Mar 3, 2024
1 parent 5364b02 commit 84a35da
Show file tree
Hide file tree
Showing 77 changed files with 675 additions and 569 deletions.
2 changes: 1 addition & 1 deletion docs/pages/docs/configuration/cosmos-config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ You can enhance your Cosmos config with autocomplete in code editors like VS Cod

```jsonc filename="cosmos.config.json"
{
"$schema": "http://json.schemastore.org/cosmos-config"
"$schema": "http://json.schemastore.org/cosmos-config",
// your options...
}
```
Expand Down
5 changes: 3 additions & 2 deletions docs/pages/docs/fixtures.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cards, Card } from 'nextra/components';
import { Card, Cards } from 'nextra/components';

# Fixtures

Expand All @@ -8,5 +8,6 @@ The fixture is the main abstraction in React Cosmos. It represents a component e
<Card title="Fixture Modules" href="/docs/fixtures/fixture-modules" />
<Card title="File Conventions" href="/docs/fixtures/file-conventions" />
<Card title="Decorators" href="/docs/fixtures/decorators" />
<Card title="UI Controls" href="/docs/fixtures/ui-controls" />
<Card title="Inputs" href="/docs/fixtures/inputs" />
<Card title="Viewport" href="/docs/fixtures/viewport" />
</Cards>
3 changes: 2 additions & 1 deletion docs/pages/docs/fixtures/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"fixture-modules": "",
"file-conventions": "",
"decorators": "",
"ui-controls": ""
"inputs": "",
"viewport": ""
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
import { Callout } from 'nextra/components';

# UI Controls
# Inputs

<Callout type="info">
Props controls are created automatically for [Node
Prop inputs are created automatically for [Node
fixtures](/docs/fixtures/fixture-modules.md#node-fixtures) in the Cosmos UI.
This enables you to tweek component props and see the result in real time
without any configuration.
</Callout>

The following fixture APIs enable creating custom UI controls in the [Control Panel](/docs/user-interface.md#control-panel).
The following fixture APIs allow you to define component inputs in the [Control Panel](/docs/user-interface.md#control-panel).

## `useValue`
## `useInput`

```jsx filename="CounterButton.fixture.jsx"
import { useValue } from 'react-cosmos/client';
import { useInput } from 'react-cosmos/client';

export default () => {
const [count, setCount] = useValue('count', { defaultValue: 0 });
const [count, setCount] = useInput('count', 0);

return <CounterButton count={count} increment={() => setCount(count + 1)} />;
};
```

The `useValue` hook can be used to control any type of serializable data, like strings, numbers and booleans, including objects and arrays.
The `useInput` hook can be used with any type of serializable data, like strings, numbers and booleans, including objects and arrays.

### Number inputs

While focused on number inputs you can use the up and down arrow keys to increment and decrement the value. Hold ⎇ for .1 increments, ⇧ for 10 increments, and ⌘ for 100 increments.

### Boolean inputs

The [Boolean Input Plugin](/docs/plugins#boolean-input-plugin) can be installed to turn boolean controls into checkboxes.
The [Boolean Input Plugin](/docs/plugins#boolean-input-plugin) can be installed to turn boolean inputs into checkboxes.

## `useSelect`

Expand All @@ -49,20 +49,19 @@ export default () => {

`useSelect` also returns a setter as the second value in the return tuple, like the `useState` hook, in case you want to change the value programatically.

<Callout type="info">
`useValue` and `useSelect` (and Cosmos in general) work great with TypeScript.
</Callout>
### Option groups

## `<Viewport>`

By using the Viewport decorator a fixture can trigger the responsive preview in the Cosmos UI on a specific resolution.
You can group options by passing an array of objects with `group` and `options` properties.

```jsx
import { Viewport } from 'react-cosmos/client';

export default (
<Viewport width={375} height={667}>
<MyComponent />
</Viewport>
);
const [color] = useSelect('color', {
options: [
{ group: 'warm', options: ['red', 'orange', 'yellow'] },
{ group: 'cold', options: ['blue', 'green', 'purple'] },
],
});
```

<Callout type="info">
`useInput` and `useSelect` (and Cosmos in general) work great with TypeScript.
</Callout>
13 changes: 13 additions & 0 deletions docs/pages/docs/fixtures/viewport.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# `<Viewport>`

Using the Viewport decorator in a fixture triggers the responsive preview on a specific resolution in the Cosmos UI.

```jsx filename="MyComponent.fixture.jsx"
import { Viewport } from 'react-cosmos/client';

export default (
<Viewport width={375} height={667}>
<MyComponent />
</Viewport>
);
```
4 changes: 2 additions & 2 deletions docs/pages/docs/index.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Callout, Cards, Card } from 'nextra/components';
import { Callout, Card, Cards } from 'nextra/components';
import StarIcon from '../../components/svg/icons/stars.svg';

# Introduction
Expand Down Expand Up @@ -27,7 +27,7 @@ React Cosmos revolutionizes component development, testing, and sharing. Its san
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
| Fixtures | A file-system based module convention for defining component states effortlessly. Enhanced with decorators. |
| User Interface | User-friendly interface for browsing fixtures. Component preview with responsive viewports. |
| Control Panel | Component data manipulation through UI controls based on component props and custom definitions. |
| Control Panel | Component data manipulation UI based on component props and custom input definitions. |
| Static Export | An interactive component library deployable to any static hosting service. |
| Integration | First-class integrations with Vite, Webpack, React Native, Next.js, and support for integrating with custom setups. |
| Plugins | Full-stack plugin system for extending every aspect of React Cosmos to suit your needs. |
Expand Down
6 changes: 3 additions & 3 deletions docs/pages/docs/plugins/fixture-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

While there's no formal way to package renderer plugins (like with server and UI plugins), you can tap into the fixture context to read and write fixture state that is synchronized between the renderer and the Cosmos UI.

The [Viewport](/docs/fixtures/ui-controls.md#viewport) decorator is a good example.
The [Viewport decorator](/docs/fixtures/viewport.md) is a good example.

- The standard fixture state object contains the `props`, `classState` and `controls` fields. They're used to construct the control panels in the Cosmos UI.
- You can extend the fixture state with extra fields. For example the [Viewport](/docs/fixtures/ui-controls.md#viewport) decorator sets the `fixtureState.viewport` field used by the responsive preview plugin in the Cosmos UI.
- The fixture state object can contain the `props`, `classState` and `inputs` fields. They're used to construct the control panel in the Cosmos UI.
- You can extend the fixture state with extra fields. For example the Viewport decorator sets the `fixtureState.viewport` field used by the responsive preview plugin in the Cosmos UI.
- Generally a fixture plugin will pair with a Cosmos UI plugin to syncronize data between the renderer, which runs inside the user's code, and the Cosmos UI.

## `useFixtureState`
Expand Down
16 changes: 8 additions & 8 deletions docs/pages/docs/user-interface.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Viewport controls that allow simulation of screen size and orientation, with the
src="/screenshots/responsive-preview.png"
/>

A screen size can be embedded into a fixture using the [`<Viewport>`](/docs/fixtures/ui-controls.md#viewport) decorator.
A screen size can be embedded into a fixture using the [`<Viewport>`](/docs/fixtures/viewport.md) decorator.

## Full-Screen Preview

Expand Down Expand Up @@ -84,7 +84,7 @@ Launches the source code of the current fixture in your default code editor.
## Control Panel

UI controls that provide powerful component data manipulation.
Inputs that provide powerful component data manipulation.

<br />
<Image
Expand All @@ -95,13 +95,13 @@ UI controls that provide powerful component data manipulation.
src="/screenshots/props-panel.png"
/>

Three types of controls are currently supported:
Three categories of inputs are currently supported:

| Control type | Description |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| Props | Automatically generated based on React element props. Only works with [Node Fixtures](/docs/fixtures/fixture-modules.md#node-fixtures). |
| Class State | Automatically generated based on React Class components, which are deprecated but supported indefinitely. |
| Custom | Defined using [custom fixture hooks](/docs/fixtures/ui-controls.md) that can be represented as text inputs or select dropdowns. |
| Inputs | Description |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Props | Automatically generated based on React element props. Only works with [Node Fixtures](/docs/fixtures/fixture-modules.md#node-fixtures). |
| Class State | Automatically generated based on React Class components, which are deprecated but supported indefinitely. |
| Custom | Defined using [fixture hooks](/docs/fixtures/inputs.md) that can be represented as text inputs or select dropdowns. |

## Adjustable Panels

Expand Down
6 changes: 3 additions & 3 deletions docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"module": "esnext",
"resolveJsonModule": true,
"paths": {
"@components/*": ["components/*"],
},
"@components/*": ["components/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
"exclude": ["node_modules"]
}
43 changes: 20 additions & 23 deletions examples/todo/src/components/TodoContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { useSelect, useValue } from 'react-cosmos/client';
import { useInput, useSelect } from 'react-cosmos/client';
import { Todo, TodoFilter } from '../types.js';

type ContextValue = {
Expand All @@ -16,32 +16,29 @@ const TodoContext = React.createContext<ContextValue>({
setFilter: () => {},
});

const defaultTodos: Todo[] = [
{
id: 1,
label: 'Work out',
done: false,
},
{
id: 2,
label: 'Do the dishes',
done: false,
},
{
id: 3,
label: 'Pay the bills',
done: true,
},
];

type ProviderProps = {
children: React.ReactNode;
};
export function TodoProvider({ children }: ProviderProps) {
const [todos, setTodos] = useValue('todos', {
defaultValue: React.useMemo(
() => [
{
id: 1,
label: 'Work out',
done: false,
},
{
id: 2,
label: 'Do the dishes',
done: false,
},
{
id: 3,
label: 'Pay the bills',
done: true,
},
],
[]
),
});
const [todos, setTodos] = useInput('todos', defaultTodos);

const [filter, setFilter] = useSelect<TodoFilter>('filter', {
defaultValue: 'all',
Expand Down
12 changes: 3 additions & 9 deletions examples/todo/src/components/TodoList/TodoItem.fixture.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import React from 'react';
import { useValue } from 'react-cosmos/client';
import { useInput } from 'react-cosmos/client';
import { TodoItem } from './TodoItem.js';

export default () => {
const [label, setLabel] = useValue('label', {
defaultValue: 'Eat the homework',
});

const [done, setDone] = useValue('done', {
defaultValue: false,
});

const [label, setLabel] = useInput('label', 'Eat the homework');
const [done, setDone] = useInput('done', false);
return (
<div className="todoapp">
<section className="main">
Expand Down
4 changes: 2 additions & 2 deletions examples/vite/src/CounterButton.fixture.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { CounterButton } from 'examples-shared/components/CounterButton.js';
import React from 'react';
import { useValue } from 'react-cosmos/client';
import { useInput } from 'react-cosmos/client';

export default () => {
const [count, setCount] = useValue('count', { defaultValue: 0 });
const [count, setCount] = useInput('count', 0);
return (
<CounterButton
suffix="times"
Expand Down
4 changes: 2 additions & 2 deletions examples/webpack/src/CounterButton.fixture.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { CounterButton } from 'examples-shared/components/CounterButton.js';
import React from 'react';
import { useValue } from 'react-cosmos/client';
import { useInput } from 'react-cosmos/client';

export default () => {
const [count, setCount] = useValue('count', { defaultValue: 0 });
const [count, setCount] = useInput('count', 0);
return (
<CounterButton
suffix="times"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import React from 'react';
import { useSelect, useValue } from 'react-cosmos/client';
import { useInput, useSelect } from 'react-cosmos/client';

export default () => {
const [name] = useValue('name', { defaultValue: 'Mark Normand' });
const [age] = useValue('age', { defaultValue: 39 });
const [comedy] = useValue('comedy', { defaultValue: true });
const [name] = useInput('name', 'Mark Normand');
const [age] = useInput('age', 39);
const [comedy] = useInput('comedy', true);
const [special, setSpecial] = useSelect('special', {
options: ['Still Got It', "Don't Be Yourself", 'Out to Lunch'],
defaultValue: 'Out to Lunch',
});
const [podcast] = useValue('podcast', {
defaultValue: {
name: 'Tuesdays with Stories',
cohost: 'Joe List',
episodes: 300,
endDate: null,
tagRegex: /itsallpipes/g,
},
const [podcast] = useInput('podcast', {
name: 'Tuesdays with Stories',
cohost: 'Joe List',
episodes: 300,
endDate: null,
tagRegex: /itsallpipes/g,
});

function renderButton(option: typeof special) {
Expand Down
Loading

0 comments on commit 84a35da

Please sign in to comment.