nuxt / nuxt/ui

Improve readability of complex API types / default values in component documentation

Open
#5,815 3 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

triage
Dominant language
TypeScript
Stars
6.9k
Forks
1.1k
Avg merge
1d 7h
Merged PRs (30d)
57

Description

Package

v4.x

Description

First of all, thank you for this great Nuxt Module and a comprehensive documentation.

I just found it a bit difficult to read some complex component prop type with inline code, and I think that we can make the documentation easier to read.

Problem

For example:

Component Prop Column Screenshot
LocaleSelect virtualize Type Image
LocaleSelect createItem Type Image
LocaleSelect ui Type Image
LocaleSelect content Default Image
Table virtualize Type Image
Table defaultColumn Type Image
Tree - Slots Type Image

Sometimes, we have inline code block with long content in:

  • API Props Type
  • API Props Default
  • API Slots Type
  • (haven't confirmed, but maybe API Emits Type as well)

Their contents can be:

  • Nested type
  • Union with a lot of members
  • Many properties
  • etc.

For props with nested type like LocaleSelect.virtualize and LocaleSelect.createItem, we can click "Show properties" to view detail.

But for props like ui, which is usually a type with a lot of properties, it's hard to read and find what properties there are.

Solution suggestion

All of the above example are rendered with <HighlightInlineType> component.

1. Use Markdown code block + format content with prettier

Screenshots
Image Image
  • ✅ Complex type / value looks better now
  • ⚠️ We need to do some string processing to the formatted content like the implementation below
  • ⚠️ We might need to give min-width to each table column, otherwise some code block will be shrinked
  • ⚠️ This component is called <HighlightInlineType>, but now it can produce type with code block
  • ❌ Prettier might be too opinionated and sometimes make the type more difficult to read. (e.g. line wrap)
  • ❌ Even for simple type like boolean, it will become a code block, which takes more spaces
    • In the screenshot of my prototype, I've tried to add a multiline check to keep simple type inline.
  • ❌ It looks ugly for prop name a inline text, while prop type in the row a code block. Their margin-top are different.
    • Maybe we can fix by give them universal margin
  • ❓ Not sure about how performance of page rendering will be affected.
    • I've measured the formatType function with preformance.now(), and it takes about 30ms in average.
  • ❓ Not sure about what is the appropriate value of the printWidth prettier config
Implementation
import prettier from 'prettier/standalone'
import typescriptPlugin from 'prettier/plugins/typescript'
import estreePlugin from 'prettier/plugins/estree'

const formatType = async (type: string) => {
  const TYPE_WRAPPER = 'type __Formatted'
  const INDENT = '  '

  let formatted: string
  try {
    formatted = await prettier.format(
      `${TYPE_WRAPPER} = ${type}`,
      {
        parser: 'typescript',
        plugins: [typescriptPlugin, estreePlugin],
        tabWidth: 2,
        semi: false,
        printWidth: 40
      }
    )
  } catch {
    return null
  }

  formatted = formatted
    .replace(/^type\s+__Formatted\s*=\s*/m, '')
    .replace(/^\|\s*/m, '') // Remove leading `|` added by union formatting

  const allLinesShareIndent = formatted
    .split('\n')
    .every(line => line.trim() === '' || line.startsWith(INDENT))

  return allLinesShareIndent
    ? formatted.replace(new RegExp(`^${INDENT}`, 'gm'), '')
    : formatted
}

onMounted(async () => {
  const formattedType = await formatType(type.value) ?? ''

  const md = formattedType.split('\n').length > 2
    ? `\`\`\`ts-type\n${formattedType}\n\`\`\`` // Code block
    : `\`\` ${type.value} \`\`{lang="ts-type"}` // Inline code

  ast.value = await parseMarkdown(md)
})

2. Use Markdown code block + simple string processing

Screenshot
Image Image
  1. Simply do line break for union
  2. For ui, simply change it to Partial<Record<TKeys, ClassNameValue>>
  • ✅ No need to execute runtime prettier format
  • ❌ Corner cases like union in nested properties are not considered
  • Others are same as ## 1.
Implementation
const formatType = (type: string) => {
  if (!type.includes('?: ClassNameValue;')) return type.replaceAll(' | ', '\n  | ')

  const keys = type.matchAll(/\s(\S+?)\?/g)
    .map(match => match[1])
    .map(key => JSON.stringify(key))
    .toArray()
    .join('\n  | ')

  return `Partial<Record<\n  ${keys},\n  ClassNameValue\n>>`
}

3. Convert only type of ui prop to <Partial<Record<TKeys, ClassNameValue>> + render in code block

Actually this is the main motivation of creating this issue. I want to have at least the ui prop to be easier to read.


Any ideas are appreciated.

Additional context

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the HighlightInlineType component and the API documentation rendering for the LocaleSelect, Table, and Tree examples named in the issue. Compare the proposed formatting approaches, including the TypeScript formatting and Markdown code-block behavior, before choosing an implementation. Done should make complex prop types, defaults, and slot types easier to scan without making simple values unnecessarily large.

Written by the indexing model from the issue text.

Assessment

Tech stack
nuxt, typescript
Domain
documentation, frontend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.