scottish-government-design-system / design-system

Design System for the Scottish Government and other Scottish public sector bodies
https://designsystem.gov.scot
MIT License
27 stars 11 forks source link

Tabs don't work on desktop #23

Open 72gm opened 1 year ago

72gm commented 1 year ago

Your sample HTML for tabs is missing a few things for the desktop version e.g. js-initialised and the classes to hide content tabs

Not sure what else is missing as the tabs won't switch - can you advise and update your documentation?

It still doesn't appear to work if I copy your actual running example code (not the sample code) so there may be something wrong with the JS also? ... another team in my org had tried this and ended up just using React Bootstrap Tab component as were unable to get this to work

A bit of clarity on this would be good.

thanks

jsutcliffe commented 1 year ago

Hello, it sounds like you're not initialising the tabs component. The 'js-initialised' class is added by JavaScript when the component is set up.

If you are including the compiled pattern-library.js file, you would want to do something like:

const tabSets = [].slice.call(document.querySelectorAll('[data-module="ds-tabs"]'));
tabSets.forEach(tabSet => new window.DS.components.Tabs(tabSet).init());

Alternatively, calling window.DS.initAll(myElement) will initialise all Design System components inside 'myElement' ('myElement' defaults to window.document if it's not specified).

72gm commented 1 year ago

Hi,

Can you provide advice on how to best import the JS into a React Typescript app?

We've had a few issues trying to get this to work so have written our own as and when we need it but going forward I'm not sure that is the best solution?

Thanks

jsutcliffe commented 1 year ago

We do not have any React expertise on the team I'm afraid. However, we are considering a move to TypeScript.

Component scripts are written as ES6 modules and can be found in the /src folder, if that helps at all. That is likely to change if we move to TypeScript, after which we would have the .ts files in /src and provide compiled JS for each component elsewhere.

72gm commented 1 year ago

If we load the javascript that is in the dist folder via a script tag it doesn't work in the application - I'm guessing because it is SIF and it would need the html to be there to attach whatever functionality it needs to attach? Which isn't going to work in a dynamic SPA? right?

The stuff that's in the src folder doesn't seem to import properly so we are probably quicker writing our own as and when required

Or am I misunderstanding?

Thanks

jsutcliffe commented 1 year ago

The JavaScript file doesn't do anything on its own, but it does add a "DS" object to the global scope. When your page is ready to set up the components, you can do window.DS.initAll().

window.DS.initAll();

You can also do that whenever the view in your SPA changes and you need to set up new components, and restrict it to the new part of the page.

window.DS.initAll(myNewSection);

If you need finer control, the classes for each component are in window.DS.components. If you wanted to set up some new accordions, for example, you would select them and then initialise them.

const myNewAccordions = [].slice.call(myNewSection.querySelectorAll('[data-module="ds_accordion"]'));
myNewAccordions.forEach(accordion => new window.DS.components.Accordion(accordion).init());
72gm commented 1 year ago

sorry meant to say... we can't do that in typescript as the DS doesn't exist at design time so we would need a different solution.. couple of teams in the org haven't been able to get this to work

72gm commented 1 year ago

tried the following after looking at the GDS guidance

image

It loaded the JS according to the Network tab but still doesn't appear to work

lewisdorigoSoSec commented 4 months ago

@72gm I think the reason that might not work is that — because React does client-side rendering of components — you may find that the elements don't exist in the DOM at the time you're running window.DS.initAll().

If you're using the design system with React, the way we've implemented this at Social Security Scotland is to import the relevant script for each component, and init based on a ref. For example, something like this:

import React, { useEffect, useRef } from 'react';
import DesignSystemTabs from '@scottish-government/design-system/src/components/tabs/tabs';

export const Tabs:ReactFC<SocialSecurityScotland.Components.Tabs> = function Tabs({
    // …props
}) {
    const tabsRef = useRef(null);

    /*
     * When component mounts, if we're in a user-space, and if the ref is set, initialise
     * the component.
     * 
     * The `tabsRef` dependency means this will only run if `tabsRef` changes. 
     */
    useEffect(() => {
        if (tabsRef.current && typeof window !== 'undefined') {
            new DesignSystemTabs(tabsRef.current).init();
        }
    }, [tabsRef]);

    return (
        <div className="ds_tabs" ref={tabsRef}>
            { /* rest of tabs code goes here */ }
        </div>
    ); 
}

export default Tabs;
72gm commented 3 months ago

@lewisdorigoSoSec yeah absolutely was... we tried a couple of ways, none satisfactory, so we just wrote our own as there wasn't much ;)