Skip to content

Step Component Pattern

David edited this page Sep 23, 2024 · 5 revisions

the new step components should be created in app/components/steps and remove the old component from app/components

where 'component' is the name of the step like answer.js and Component is like Answer

app/components/steps/component.js:

export default function ComponentStep(props){
  const {data, upsert}=useContext(DeliberationContext)
  // fetch data from the server and upsert it into the context if/when it arrives
  // before a response from the server the component should return null and not render anything
  useEffect(()=>{ window.socket.emit('fetch-api-handle',p1 ,p2, ..., results=>upsert(results)),[])
  function handleOnDone({valid, value, delta}){ // uses upsert, calles socket-apis  ...}
  // adding a new prop to onDone called delta.  This is what the user had changed, either the rank or point, were as value would be all the things in a list.
  // the render function should add a delta prop, this should be just the chanaged point or rank or whatever. 
  // The handleOndone should have code to upsert the delta into the database through an api, and into the context. 
  return <Component {...deriver(data, local), onDone: handleOnDone, ...props} />
}
export function Component(props){
// this is the render function, mostly what's there now
// props of the old component will need to change to match the new props define in the spread sheet - that are the output of the deriver
// stories should test this function
// it important to differentiate between props that are empty and props that are undefined.  Undefined means we are waiting for the database query, so the component should not reender anything.  empty, like {} or [] means the user hasen't made any entries yet. If there are undefined props, return null - don't render anything for now.
}

// in the spreadsheet each deriver has a name, but it's not necessary to use a different name since it's local to this file
export function deriver(data){
  const local = useRef({}).current   //  keeping track of previous values in local, 
  // returns props for the render function, taken from data, 
  // being careful not to change ref's to objects that aren't different
    return {} 
}

app/components/steps/__tests__/component.js:

  import {deriver} from '../component.js'
  ...

create jest tests for the deriver

  1. input data undefined
  2. input data empty
  3. input data present - output data not there yet
  4. input data unchanged
  5. subset(s) of input data changes testing that only the affected part of the output has changed

stories/component.stories.js:

  1. update to import and test Component, rather than ComponentStep
  2. update args to match the updated props of the Component

stories/component-step.stories.js:

  1. import and test ComponentStep
  2. use the DeliberationContext decorator from comomon.js
  3. create mock api calls for the fetch api and action apis. See https://github.com/EnCiv/enciv-home/blob/0d5617973d1cd5c7d9f2b9d7cd5eb959c7bcaccd/stories/frequently-asked-questions.stories.js for how to mock window.socket.emit
  4. create tests for
  • the case when the server hasn't returned data yet
  • the initial (no data) use case
  • the returning user case
  • the case where the user enters initial data
  • the case where the user updates existing data
Clone this wiki locally