import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';

import ValueTransformer from '.';
import Field from '../Field';
import WithValue from '../WithValue';
import ValueSubscriber from '../ValueSubscriber';
import ExampleForm from '../../../docs/ExampleForm';

<Meta title="Components|ValueTransformer" component={ValueTransformer} />

# ValueTransformer

<Props of={ValueTransformer} />

The `ValueTransformer` allows you to use different values for rendering than you
use for your form submissions. This may be used for grouping values into a cleaner
interface, for displaying phone masks, or even converting a group of values into
a JSON string for submission.

<Preview>
  <Story name="default">
    <ExampleForm defaultValues={{ transform: '{"lookMa":"No JSON here!"}' }}>
      <ValueTransformer
        name="transform"
        transformOnChange={value => {
          return JSON.stringify(value)
        }}
        transformValue={value => JSON.parse(value)}
      >
        <Field name="lookMa" />
      </ValueTransformer>
    </ExampleForm>
  </Story>
</Preview>

### Advanced Usage

<Preview>
  <Story name="advanced usage">
    <ExampleForm
      defaultValues={{
        transform: [
          { value: 'test', group: '3' },
          { value: 'inner', group: '1' },
          { value: 'value', group: '3' },
        ],
      }}
    >
      <ValueTransformer
        name="transform"
        transformOnChange={values => values.reduce((acc, next) => (
          acc.concat(next.values.map(value => ({ value, group: next.group })))
        ), [])}
        transformValue={(values) => {
          // Transformed will be an ordered array
          const transformed = [];
          // We use a reduce into an object, keyed by our group property to reduce
          // the time spent looking for an existing group
          values.reduce((acc, next) => {
            if (!acc[next.group]) {
              // If we haven't seen this group before, add it to the object and push
              // the new object into our ordered array.
              acc[next.group] = { values: [], group: next.group };
              transformed.push(acc[next.group]);
            }
            // Push the value into the group
            acc[next.group].values.push(next.value);
            return acc;
          }, {});
          return transformed;
        }}
      >
        <h4>Values after transformation</h4>
        <WithValue>{value => <pre>{JSON.stringify(value, null, '  ')}</pre>}</WithValue>
        <br />
        <WithValue>
          {values => values.map((value, i) => (
            <ValueSubscriber key={i} name={i}>
              <Label htmlFor="group">Group: </Label>
              <Field name="group" />
              <WithValue name="values">
                { items => JSON.stringify(items)}
              </WithValue>
              <br />
            </ValueSubscriber>
          ))}
        </WithValue>
      </ValueTransformer>
    </ExampleForm>
  </Story>
</Preview>

### Conditionally removing values

In some cases, you may only want to have a value in the top-level object in certain conditions, such as cleaning your data to remove empty values before submitting. This can be done by using the `ignoreUndefined` property that comes from [ValueSubscriber](/?path=/docs/components-valuesubscriber--default-story) and providing a default value in your `transformValue` prop. Since this will be creating a new value on every render, we recommend that you store an empty object/array variable outside so that the reference doesn't change.

<Preview>
  <Story name="conditional values">
    <ExampleForm defaultValues={{}}>
      <ValueTransformer
        name="conditionalField"
        ignoreUndefined
        transformOnChange={value => (value ? value : undefined)}
        transformValue={value => value || ''}
      >
        <Field />
      </ValueTransformer>
    </ExampleForm>
  </Story>
</Preview>
