Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

afformative

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

afformative

A standardized way to format values in your React components.

  • 0.6.0-alpha.3
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
62
increased by19.23%
Maintainers
1
Weekly downloads
 
Created
Source

afformative

✅ ✍️ 👀

A standardized way to format values in your React components.

Afformative helps UI component libraries visualise arbitrary data in a reusable, plug-and-play fashion. Formatting dates and enumeration translations in your select fields and data grids has never been easier.

MIT License Version

Installation

Use either of these commands, depending on the package manager you prefer:

yarn add afformative

npm i afformative

Quick Start

I'll try not to bore you too much, I promise.

The Holy Standard

Thou shalt not format thy values without afformative.

A formatter is an object with a .format method. Formatters should be created solely using the makeFormatter factory.

import { makeFormatter } from "afformative"

const upperCaseFormatter = makeFormatter(value => value.toUpperCase())

upperCaseFormatter.format("foo") // "FOO"

Consume formatters in your UI component library using a conventional formatter prop.

import { makeFormatter } from "afformative"

// This is usually the best default formatter.
const identityFormatter = makeFormatter(value => value)

const Select = ({ formatter = identityFormatter, items, ...otherProps }) => (
  <select {...otherProps}>
    {items.map(item => {
      const text = formatter.format(item)

      return (
        <option key={item} value={item}>
          {text}
        </option>
      )
    })}
  </select>
)

Usage Context

Although formatters can render icons or custom translation components, we often need to access primitive data instead of React elements.

Lexicographic sorting of items based on translations is a typical real world example, especially when you are using a custom React component for visualising the translation keys alongside the actual translations.

This is where usage suggestions comes into play. Suggestions can be used to tell formatters that a value needs to be rendered with some special care. For example, pass "primitive" to tell a formatter that it should return a primitive value, such as a string.

import { makeFormatter } from "afformative"

const booleanFormatter = makeFormatter((value, usageSuggestions) => {
  if (usageSuggestions.includes("primitive")) {
    return value ? "True" : "False"
  }

  return <Icon type={value ? "success" : "failure"} />
})

booleanFormatter.format(true) // <Icon type="success" />
booleanFormatter.format(true, ["primitive"]) // "True"

You can also pass arbitrary data to formatters as the third argument: data context. Let's use a dummy table component as an example.

const Table = ({ rows, formatter = identityFormatter }) => (
  <table>
    {rows.map(row => (
      <tr>
        {row.map((cell, cellIndex) => (
          <td>{formatter.format(cell, [], { row, cellIndex })}</td>
        ))}
      </tr>
    ))}
  </table>
)

Data context allows the users of this table component to write purpose-built formatters, making it possible to take other values in the same row into account. For example, the following formatter would change the color of the cell value based on the previous value in the same row.

const rowTrendFormatter = makeFormatter((value, suggestions, { row, cellIndex }) => {
  if (cellIndex === 0) {
    return <span>{value}</span>
  }

  const previousValue = row[cellIndex - 1]

  return <span style={{ color: value >= previousValue ? "green" : "red" }}>{value}</span>
})

Of course, this formatter only makes sense for our table component, nowhere else.

Because row and cellIndex are passed as the data context, the formatter still receives just the cell value as its first parameter! This allows us to pass other generic formatters (e.g. a currency formatter) to the table component without having to worry about the value structure.

Accessing React Context Reliably

Use hooks.

const useEnumFormatter = enumType => {
  // `useSelector` is from `react-redux`, `useIntl` from `react-intl`.
  // `makeSelectEnumTranslationKeys` is a made-up Redux selector factory.
  const enumTranslationKeys = useSelector(makeSelectEnumTranslationKeys(enumType))
  const intl = useIntl()

  return useMemo(
    () =>
      makeFormatter(value =>
        intl.formatMessage({
          defaultMessage: value,
          id: enumTranslationKeys[value],
        }),
      ),
    [intl, enumTranslationKeys],
  )
}

const someEnumFormatter = useEnumFormatter("someEnum")

Consuming Formatters

As mentioned earlier, formatters should be passed to components using the conventional formatter prop. Alternatively, if you need to pass multiple formatters (e.g. in case of column definitions), pass an array of objects where each object has a formatter property.

All formatters also expose the .wrap method. You can use this method to alter the behaviour of the formatter for some specific values.

const useSnowflakeAwareFormatter = formatter => {
  const intl = useIntl()

  return useMemo(
    () =>
      formatter.wrap((delegate, value) => {
        if (isSnowflake(value)) {
          return intl.formatMessage(messages.snowflake)
        }

        return delegate(value)
      }),
    [formatter, intl],
  )
}

Changelog

See the CHANGELOG.md file.

License

All packages are distributed under the MIT license. See the license here.

Keywords

FAQs

Package last updated on 27 Mar 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc