carbon-design-system / carbon-spec

[WIP] Specification for the Carbon Design System
Apache License 2.0
7 stars 6 forks source link

Component Directory Structure #6

Open scottnath opened 5 years ago

scottnath commented 5 years ago

Is your feature request related to a problem? Please describe. Component directory structure should be architected in such a way that the content of a component, be it files or data, is consistently written and structured and available for straightforward external usage.

Describe the solution you'd like Documentation which we can refer to which says what files would be where. ie: sass, written requirements, requirements-tests would be in spec, handlebars templates in carbon-components, react versions in carbon-c-react, etc.

Also...regardless of the location, there should be an easy way to import these files and component-data from a single location. So, if I pulled in carbon-spec/src/accordion I could find that files like user-requirements.feature, index.scss (instead of _accordion.scss), _mixins.scss (etc) within that directory. There could also be a node file, index.js, which would output data required by the component - for instance content models, objects describing each variant or scenario, etc.

I'll add a comment below which contains the gherkin we used when architecting our component directories.

Describe alternatives you've considered n/a

Acceptance Criteria

scottnath commented 5 years ago

This is the set of requirements we put together to track the various pieces of a component. It needs to be updated because there are many many more files and more data associated with a component imho - but this is a good start for the basics of what's required

Feature: Cross-team and cross-technology pattern
  As a developer
  I want to document how we will store and distribute my team's patterns
  So that my team can document, store, and build upon my team's efforts

  Scenario: Module exports an object
    Given that I have a pattern for use in multiple projects and scenarios
      And that my pattern will be distributed as an npm module
      And that my pattern must include a content model
      And that my pattern may be designed in Sketch
      And that my pattern may be designed in Adobe Illustrator
      And that my pattern may be built using nunjucks templating
      And that my pattern may be built as a React component
      And that my pattern may be built as an Angular 1x component
      And that my pattern may be built as an Angular 2x component
      And that my pattern may include a style sheet
     When I release my pattern for use
     Then it should be released as an npm module
      And said module should export an object
      And said object may have a key who's value represents the Sketch file
      And said object may have a key who's value represents the AI file
      And said object must have a key who's value represents the canonical version
      And said object may have a key who's value represents the nunjucks version
      And said object may have a key who's value represents the React version
      And said object may have a key who's value represents the Angular 1x version
      And said object may have a key who's value represents the Angular 2x version
      And said object should have a key who's value represents the content model
      And said object should have a key who's value is a function which returns an object of content
      And said content object should return example content
      And said content object should be addressable by key
      And said content function should accept parameters to vary what is returned.

  Scenario: Pattern has dependencies
    Given that I have an interface that my healthcare ux pattern that includes a form
      And that my app is built using components
      And that portions of the form may be built from components in an external component library
     When my app imports my healthcare pattern
     Then it should import the external library as a dependency
      and my app should have access to all styles required by my pattern.      

  Scenario: Pattern is connected to a clearly defined User Story
    Given that I have a pattern for use in multiple projects and scenarios
      And that my pattern should have been created according to one or more user stories
      And that said stories must be written for a specific user
      And that said users should be one of our pre-determined personas
     When I am creating documentation on my pattern's creation
      And said pattern should be distributed with documentation
      And said documentation should include all relevant user stories
      And said documentation should include references to represented personas
      And said documentation may be in the module's README.

  Scenario: Canonical Design
    Given that I need a source-of-truth to represent my pattern's design
      And that said source should include example content
      And that said source should have UX to match the user story
      And that said source should have a design that follows Watson Health guidelines
      And that said guidelines can be enforced using Sketch libraries
     When I want an design representing the new feature as described in the user story
     Then I need my design to be created in Sketch
      And said design will be represented by a single Sketch file
      And said Sketch file should include all libraries which enforce Watson Health guidelines
      And said Sketch file should accept content
      And said content should match the story's content model
      And said design should represent all of the story's UX requirements
      And said design should conform to Watson Health guidelines
      And said design should represent all content variations.

  Scenario: Canonical Implementation
    Given that I need a source-of-truth to represent my pattern's implementation
      And that said source should include example content
      And that said source should be styled to match the design
     When I want an example representing my pattern
     Then I need my example to be written in semantic HTML
      And said HTML should contain all elements required by my pattern
      And said elements should contain all attributes required by my pattern
      And said pattern should accept content
      And said content should conditionally change the viewed pattern
      And said example should be tested to match the user story requirements.

  Scenario: Pattern is implementation agnostic
    Given that I have a pattern for use in multiple projects and scenarios
      And that said projects may be implemented using disparate technology solutions
      And that said solutions should create consistent HTML markup
      And that said solutions should follow a consistent element class naming structure
     When I am creating an implementation-specific version of my pattern
     Then my new version should have the same markup as the canonical version
      And said version should accept content
      And said content should conditionally change the viewed pattern
      And said version should match the canonical when viewed by a user
      And said version should match the canonical when unit-tested
      And said version should be tested to match the user story requirements.

  Scenario: Pattern should match external versions
    Given that my pattern may be implemented by external repositories
      And that external implementations should produce the same HTML as my canonical version
     When testing my pattern
     Then my tests should include the external implementation
      And the external version should be run through the default unit tests for my implementation
      And there should be a way to determine inconsistencies between implementations.

  Scenario: Anatomy of a pattern
    Given that I have a pattern for use in multiple projects and scenarios
     When I am creating a home for my pattern
     Then that pattern should be use-able via multiple technologies
      And said pattern should be distributed with a README
      And said README should include usage instructions
      And said pattern's module should include documentation
      And said pattern's module should export an object
      And said pattern's module should include a canonical design
      And said pattern's module should include a canonical implementation
      And said pattern's module may include multiple implementations
      And said module's patterns should be tested against external pattern implementations
      And said multiple implementations must pass the same tests as the canonical version.

Cross-team and cross-technology pattern

scottnath commented 5 years ago

also for reference: https://github.com/alphagov/govuk_publishing_components/blob/master/docs/component_principles.md

elizabethsjudd commented 5 years ago

Items specific to the carbon-spec repo:

In a carbon-components-X repo

Main pain point: in this current setup the only way to verify the exportable tests is by working in parallel with a carbon-component-X repo since the template and demos are defined there. I feel like we wouldn't want static HTML as the carbon-specs repo since there are many demos you'd want to test for thoroughness of variants, states, modifiers, etc so I'm not sure if there is away around this.

scottnath commented 5 years ago

demos (variants) source

There should be a canonical set of demos (variants). For instance, button has multiple types as listed by Carbon's next site: https://next.carbondesignsystem.com/components/button/usage

They are:

If all of these items are something a developer should expect to be available in their framework of choice, then there should be a configuration that breaks down each variant the should be part of a given component's spec. These would also be reflected within the selectors object.

Point being...under In a carbon-components-X repo section, this line: configuration file that sets up demos/documentation specific the framework should prol be: configuration file that sets up additional demos/documentation specific the framework

and Items specific to the carbon-spec repo: should have a bullet-point that clearly denotes it is the source for all variants that each framework should expect to have developed. These would also be reflected via the tests the spec repo contains as well as the variant selectors available via the selectors object.

generating a config object

This line: contains a function that can take a configuration object and output a template context object (generate) is framework specific. Each framework configures their template/module/component/meow differently, but we could share the pre-generate configuration object across frameworks. So, before we generate out handlebars-specific template configuration, we have some a set of parameters which are more generic:

{
  variant: 'danger',
  icon: 'information',
  content: 'Danger button'
}

in our generate function that produces configuration that will create a button in the danger variant styles that has an information icon with the content of the button being Danger button. It would make sense if each framework shared a similar api. That theory would mean that the React version which now accepts this:

<Button kind="danger" {...setProps}>
  Danger button
</Button>

could align by accepting this:

<Button variant="danger" icon="information">
  Danger button
</Button>

or conversely, the Handlebars version could align by exchanging variant with kind.

helps the tests!

Both of the above get us closer to a canonical set of variants (kinds? demos?). So your pain point of " in this current setup the only way to verify the exportable tests is by working in parallel with a carbon-component-X repo since the template and demos are defined there." can be fixed if carbon-specs contains the demos.

helps the shares :)

This also means that if carbon-components-meow has a new button variant, and that variant is something approved for global use across the carbon frameworks by being accepted by IBM design...then it could be moved to carbon-specs as a voluntary feature, that can be tested for, but maybe not fail anyone's compliance until it's deemed a required feature.

elizabethsjudd commented 5 years ago

@scottnath here is my updated list. My only concern is the template in the spec repo...

  1. Do we intend for it to be static HTML? if not what is it without adding in framework specific work?
  2. How would we show the configuration setup demos with static HTML?
  3. The maintenance to up keep these static HTML demos where they aren't consumable doesn't seem realistic.

carbon-spec repo:

In a carbon-components-X repo

joshblack commented 5 years ago

Just a heads up @scottnath, in this example for the React API:

<Button variant="danger" icon="information">

We actually have to move away from the icon name API as it's a pattern that isn't easily tree-shaken 😞 In order to implement, we have to do a pattern like:

import AllIcons from 'icons-package';

export default function MyComponent({ icon }) {
  return (
    <>
      <p>Some text</p>
      {AllIcons[icon]}
    </>
  );
}

Which means that we have a runtime lookup 😞

joshblack commented 5 years ago

Just catching up here, but one thing I've been thinking about is if spec should export anything, or if it should be an aggregator.

One hope of mine with this project would be to support a view like: https://test262.report/ where, instead of engines, we should framework implementations and their corresponding coverage. The way we could report on frameworks is by having them PR into spec. A structure could be:

The project itself would be responsible for three assets:

elizabethsjudd commented 5 years ago

@joshblack I'm following the idea you have of it containing the reports for the each implementation/framework and I think the report like test262 is a very interesting idea. I do think that the spec repo would be a bit more though than just these reports. To my understanding this repo was meant to be the location of all things that are common despite what JS framework is being used. I'm also a little curious as to what the work flow would be for this to make sure that what's in spec and what's in the framework specific repo are always in sync.

As far as the spec document goes I think we need to do something different than the aria site. It's not clear what "user needs" are, expected behavior, etc and in general it's a bit daunting. During my a11y meetings the one consistent thing that people talk about is how the standards documentation is not clear. I think there are aspects of what they are doing that would could certainly pull in but that we will want to re-think the structure and content that is actually included.

mattrosno commented 5 years ago

@joshblack I don't see why we couldn't have top-level spec (consumed by carbon-components-x for implementation), test (consumed by carbon-components-x for spec verification), and reports (for carbon-components-x to PR to) as top-level directories. Unless that third part gets too noisy, and we need something separate to track spec adherence.

Side note: maybe the spec adherence reporting would better live in a separate repo where we track other design system KPIs for adoption and contributions. Framework spec coverage will depend on strong contributions after all.

@scottnath @elizabethsjudd I move discussion on generate functions to #8. Looking forward to hearing your thoughts.

scottnath commented 5 years ago

canonical set of demos (variants). Written how?

@elizabethsjudd this goes to your questions about how we create the static HTML. These should not be files written by a human. Ideally, these files would be autogenerated from a framework. It seems though, that this is not really something that would be only coming from one framework:

Scenario: angular team finds and fixes a bug in button

Steps to change canonical HTML:

  1. bug in button is found and submitted to Carbon as a story
  2. angular team changes their button component, adding some meow attribute, which fixes the bug
  3. on merge in c-c-angular, their CI creates static HTML from the Button angular module and compares their static HTML output to the canonical set of HTML files in carbon-spec
  4. on difference, a PR is auto-created into carbon-spec containing updates to the static HTML files
    • we do this now with VRT differences
  5. the carbon-spec PR fails its CI because the static HTML does not match the tests
  6. angular team updates tests on carbon-spec (this step could happen at any time during the above steps)
  7. new button spec is merged (after extensive and thorough review)
  8. static HTML PR created from angular repo now passes carbon-spec tests
  9. all other frameworks now require an update to be in sync with canonical

points being:

configuration file for demos (variants). Written how?

Luckily all the frameworks use javascript, so it can be an ingestible object. This would require some level common API between the systems though. Sounds like that might be tricky in the short term though, so this may fall on the tests.

tracking scenarios within a framework

  1. there is a secondary button test that tests HTML of a secondary button
  2. runs VRT on secondary button
  3. unit tests determine expected attributes and classes (classes=bx--btn bx--btn--secondary, attributes=(only non-framework specific attributes are checked, all others ignored)
  4. each framework would have a status breakdown for each component...so something like this chart would be on a tab for each component, but would detail all the demos that a carbon-framework has submitted for testing

reports directory

Do we need these reports to be files? Can the reports all be within a framework's CI process? Output could be created and visible via site on github-pages or carbon..com perhaps.

Regardless...we would expect regular changes to be happening across all frameworks and if there is a process of PRs for merging new report files, that could make the reporting lag behind the actual code in the frameworks. Live snapshots could reduce this delay.

configuration object vs template/framework config object

For clarification, we should define:

"configuration object": refers to a non-framework specific configuration object that would be ingested by a framework and used to create the output.

So... @joshblack to your point about a runtime issue in React in finding the right icon - my React example was made without deep knowledge of React. The real goal is that for someone using carbon-components-react to create an app who wanted to create a button could use the configuration equaling:

{
  variant: 'danger',
  icon: 'information',
  content: 'Danger button'
}

to create a React-created danger button with the info icon and Danger button for the content.

So when we say the frameworks should share an input api - that api at the component level can be referred to as a "component's configuration object".

elizabethsjudd commented 5 years ago

@joshblack @scottnath @mattrosno See this https://github.com/carbon-design-system/carbon-spec/issues/8#issuecomment-458642152 for more around Scott's comment on configuration object vs template/framework config object

joshblack commented 5 years ago

To my understanding this repo was meant to be the location of all things that are common despite what JS framework is being used.

@elizabethsjudd I think something that would be valuable is to decouple implementation details from the spec. It would be great if we could have it so that anyone could implement the spec, but we have a canonical implementation that follows it if you don't want to do it custom. Decoupling things in this way could mean that we could have potentially multiple implementations that could work in various contexts that still adhere to the spec. One example could be around styles, where one style implementation would want to leverage sass while another one wants to leverage a CSS-in-JS solution.


@mattrosno Would 100% make sense to export the runner if implementations wanted to use it in their own projects 👍 Could easily see new framework XYZ wanting to follow TDD with:

/* Test file */

// Proposed API
import { runner, runComponent } from '@carbon/spec';

describe('ComponentName', () => {
  test('spec', () => {
    // Run specific component check
    runComponent('ComponentName', /* mount fn */);
  });
});

And working interactively to make sure it hits all the necessary rulesets.

elizabethsjudd commented 5 years ago

@joshblack all the conversations we've had to this point were that Sass was also going to live in this repo. Breaking out Sass to allow something like you're describing does make sense in flexibility, it's just new to these conversations and we'd want to figure out where Sass would live than.

joshblack commented 5 years ago

I think in terms of workflow it'd be great if we could keep the styles colocated with a canonical implementation versus in the spec, mostly because it can be easier to create component permutations and debug interactive styles when combined with a framework or environment like fractal.

Things like exporting selectors then can be tied to the style implementation versus the spec. This could be valuable if, once again, teams want to drive forward with an alternative style implementation that still adheres to the spec. This could help if things like selectors mean different things to a framework using sass versus something using CSS-in-JS.

mattrosno commented 5 years ago

As we get deeper into the proof of concept, I hope this will be more clear. Should (1) the spec be used just for component requirements, exported test runner and test reporting purposes? Or (2) should the spec also provide configuration objects (element, attributes, classNames, content) when carbon-components-x asks for them.

I'll continue to push on (2) in the proof of concept.