dudley
dudley

Reputation: 53

how to create new url by check tab in react

When I check the tab in the checkbox, the url is created according to the id. I want to show only that tab when the url checked. But the problem is I don't know how to create url when more than two tabs are checked. What logic or library do I need?

function App() {const [tabs, setTabs] = useState([
    { id: 'XYZ', name: 'Prediction', active: true, checked: true },
    { id: 'XY1', name: 'Model', active: false, checked: false },
    { id: 'XY2', name: 'Model2', active: false, checked: false }]);
    const [activeTab, setActiveTab] = useState('XYZ');
    const selectUrlCheck = (e, id) => {
    const checkedIndex = tabs.findIndex(tab => tab.id === id);

    const newTabs = [...tabs];
    if (newTabs[checkedIndex].checked === true) {
      newTabs[checkedIndex] = { ...newTabs[checkedIndex], checked: false };
      setTabs([...newTabs]);
    } else {
      newTabs[checkedIndex] = { ...newTabs[checkedIndex], checked: true };
      setTabs([...newTabs]);
    }};
  const tabToggle = e => {
    setActiveTab(e.target.id);
};
  return (
    <AppWrapper>
      <TopNavigation
        tabs={tabs}
        selectUrlCheck={selectUrlCheck}
        tabToggle={tabToggle}
        activeTab={activeTab}
        url={window.location.href}
      />
      <ContentWrapper>
        <Suspense fallback={<div>Loading...</div>}>
          <Switch>
            <Route
              exact
              path="/"
              render={() => <Redirect to={`/predict/${activeTab}`} />}
            />
            <Route path="/predict" component={PredictPageRouter} />
            
            <Route path="/preview" component={PreviewPage} />
 
          </Switch>
        </Suspense>
      </ContentWrapper>
    </AppWrapper>
  );
}

const TopNavigation = ({ tabs, tabToggle, activeTab, url, selectUrlCheck }) => {
  const [publishingModal, setPublishingModal] = useState(false);

  const modalOpen = () => {
    console.log('modal Open');
    setPublishingModal(true);
  };

  return (
    <>
      <PublishingModal
        url={url}
        tabs={tabs}
        selectUrlCheck={selectUrlCheck}
        open={publishingModal}
        onCloseModal={setPublishingModal}
      />
      <Wrapper>
        <TopBarWrapper>
          <LogoWrapper>
            <LogoLink to="/">
              <Img
                src={AlgoLogo}
                alt="logo"
                style={{ width: '27px', height: '27px', marginTop: '-5px' }}
              />
              <LogoText>dashboard</LogoText>
            </LogoLink>
          </LogoWrapper>
        </TopBarWrapper>
        <TopTabWRapper>
        
          <TabWrapper>
            {tabs &&
              tabs.map(({ id, name }) => (
                <LogoTab to={`/predict/${id}`} key={id}>
                  <TabLogoText
                    id={id}
                    onClick={e => tabToggle(e)}
                    className={activeTab === id ? 'active' : ''}
                  >
                    {name}{' '}
                  </TabLogoText>
                </LogoTab>
              ))}
          </TabWrapper>
          <div>
            <PublishingBtn onClick={modalOpen}>
              <Icon name="share alternate" />
              <span>publishing url</span>
            </PublishingBtn>
            <GoToModelDataLink>
              <Icon name="setting" />
              management
            </GoToModelDataLink>
          </div>
        </TopTabWRapper>
      </Wrapper>
    </>
  );
};

const PublishingModal = ({ open, onCloseModal, url, tabs, selectUrlCheck }) => {
  if (!open) return null;
  return (
    <>
      <OverLay />
      <Bg>
        <Root>
          <ModalHeader>
            <IconImg
              role="button"
              src={CloseIcon}
              alt="close modal"
              onClick={() => onCloseModal(false)}
            />
          </ModalHeader>
          <ModalContent>
            <TitleArea>
              <Title>publishing</Title>
            </TitleArea>
            <ShareContent>
              <ClipBoardInput
                value={url}
              />
              <CopyToClipboard text={url}>
                <CopyClipBtn>
                  <Icon name="copy" />
                </CopyClipBtn>
              </CopyToClipboard>
              <CopyToClipboard text={url}>
                <ShareBtn>
                  <Icon name="share alternate" />
                  <span>share</span>
                </ShareBtn>
              </CopyToClipboard>
            </ShareContent>
            <SelectH2>Select pages to publish</SelectH2>
            <CheckBoxWrapper>
              {tabs.map(tab => (
                <TabWrapper key={tab.id}>
                  <UrlCheckbox
                    name={tab.name}
                    id={tab.id}
                    checked={tab.checked}
                    onChange={selectUrlCheck}
                    label="checkbox"
                  />

                  <ModelSpan>{tab.name}</ModelSpan>

                  {* UrlBox component is checkbox *}
                  <UrlBox>{`${window.location.origin}/predict/${
                    tab.id
                  }`}</UrlBox>
                </TabWrapper>
              ))}
            </CheckBoxWrapper>
          </ModalContent>
        </Root>
      </Bg>
    </>
  );
};


In modal, there is the url of each tab in the check box, and if you check the check box, a new url is created, and if you share the URL to the customer accordingly, only the checked tab is rendering.

Upvotes: 0

Views: 751

Answers (1)

Linda Paiste
Linda Paiste

Reputation: 42218

There's a lot of ways to approach this. You could use query parameters like "/predict/?model=XYZ,XY1". You could use a stable URL like "/predict" and pass the models as state.

Mapping Slugs

The code that I am going to create here uses a combined slug. A single checked tab would lead to "/predict/XYZ" while checking multiple tabs creates a URL like "/predict/XY1-XYZ. This format assumes that the id itself never contains a hyphen -. If that is incorrect then you can rewrite these functions to something different.

// convert an array of ids into a string slug
function idsToSlug( ids = [] ) {
  // I am sorting them to enforce consistent slugs regardless of the order of the array
  // [...ids] prevents mutation by sorting a copied array
  return [...ids].sort().join('-');
}

// convert a string slug into an array of ids
function slugToIds( slug = "" ) {
  return slug.split('-').filter( s => s.length > 0 );
}

Accessing Active Ids

Our PredictPage component can get the slug from the URL and use it to lookup which tabs to include.

function PredictPageRouter() {
  // this will be a string
  const {slug = ""} = useParams();
  // break string into array
  const activeIds = slugToIds(slug);

  /* ... */
}

I am confused about why you have the tabs themselves in a useState. Are they a constant? Or do they come from some external source that changes? If it's a changing state then how does your PredictPageRouter work when aren't passing it tabs as a prop? I am going to assume that only active and checked are stateful and that the tabs themselves are some app-wide constant.

const TABS = [
  { id: 'XYZ', name: 'Prediction' },
  { id: 'XY1', name: 'Model' },
  { id: 'XY2', name: 'Model2' },
];

Assuming a global TABS constant, we can search for a tab by id.

function findTab( id ) {
  const tab = TABS.find(
    // case-insensitive matching
    tab => tab.id.toLowerCase() === id.toLowerCase()
  )
  // throw an error if tab not found
  if ( ! tab ) {
    throw new Error("invalid tab id " + id);
  }
  return tab;
}

Here's a dummy component to check the routing.

function TabContent({activeIds}) {
  // lookup tab objects
  try {
    const tabs = activeIds.map(findTab);
    // do whatever with the tabs
    return (
      <div>Showing Tabs: {tabs.map(tab => tab?.name).join(', ')}</div>
    )
  } catch (error) {
    return (
      <div>ERROR: {error.message}</div>
    )
  }
}

Updating Active Ids

The activeIds come from the URL, so we don't need a local state. Instead we want to update the URL in response to check/uncheck actions. Here I am using history.push.

function TabNavigation({ activeIds }) {
  const history = useHistory();

  const handleChange = (id) => (e) => {
    const newIds = e.target.checked
      ? // add id on check
        [...activeIds, id]
      : // remove id on uncheck
        activeIds.filter((active) => active.toLowerCase() !== id.toLowerCase());

    // push new ids to history
    history.push("/predict/" + idsToSlug(newIds));
  };

  return (
    <div>
      {TABS.map((tab) => {
        const isActive = activeIds
          .map((id) => id.toLowerCase())
          .includes(tab.id.toLowerCase());
        return (
          <div key={tab.id} className={isActive ? "active" : ""}>
            <input
              type="checkbox"
              checked={isActive}
              onChange={handleChange(tab.id)}
            />
            <span>{tab.name}</span>
          </div>
        );
      })}
    </div>
  );
}

The PredictPageRouter might render something like this:

function PredictPageRouter() {
  // this will be a string
  const { slug = "" } = useParams<{ slug?: string }>();
  // break string into array
  const activeIds = slugToIds(slug);

  // can render here or pass off to some other component
  return (
    <div>
      <TabNavigation activeIds={activeIds} />
      <TabContent activeIds={activeIds} />
    </div>
  );
}

Interactive CodeSandbox Demo

Upvotes: 1

Related Questions