Dashboards & Visualizations

How can we create Tabs using Link List in Splunk with only CSS Override NO SplunkXML JS Extension?

niketn
Legend

The Splunk Dev Blog by Luke Murphy provides details on Making a dashboard with tabs (and searches that run when clicked). It uses Simple XML CSS and JS extensions to create Tabs in Splunk Dashboard through Splunk Dashboard Tabs Example code shared on Github.

This question is more to document a different approach with the use of Splunk Link List input with CSS Override and Link List change event handler to make it appear and function like a Tab in Splunk. This does so without using JS 🙂

You can have many Link Lists within same Dashboard which implies many tabs within same Dashboard as per your need and implementation. Hope you find this useful 🙂

alt text

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"
1 Solution

niketn
Legend

Following is a run anywhere example based on Splunk's _internal index.

Step 1. Create a panel with Link Input as per the requirement of tabs. In above example Sourcetype, Component, Log Level are three tabs.
Step 2. Set the required token from Link input in SPL or through <change> event handler of the Link Input to change SPL or hide/show panel using depends respectively. For simplicity, this example changes underlying SPL, there are several examples of depends/rejects on Splunk Docs, Blogs and Splunk Answers.
Step 3. Add <html> panel with horizontal line <hr> to mark border of Link input.
Step 4. Apply CSS override (can be through dashboard <style> section for testing/simplicity or prebuilt-panel, or static CSS file)

PS: CSS applied as per 7.1.x + and may also need some tweaking for Dark Theme vs Light Theme (white Border is used in the Dark Theme example).

alt text

Following is the run anywhere dashboard example using Simple XML and CSS (NO JS REQUIRED). Please try out and let me know any feedback 🙂

<form theme="dark">
  <label>Tabs using Links</label>
  <!-- Base Search to Pull Error Data -->
  <search id="bAllErrorData">
    <query>index=_internal 
| bin _time span=1h
| stats count by _time sourcetype component date_hour log_level
    </query>
    <earliest>$tokTime.earliest$</earliest>
    <latest>$tokTime.latest$</latest>
  </search>
  <fieldset submitButton="false">
    <input id="input_time" type="time" token="tokTime">
      <label></label>
      <default>
        <earliest>-24h@h</earliest>
        <latest>now</latest>
      </default>
    </input>
  </fieldset>
  <row>
    <panel id="panel_layout">
      <input id="input_link_split_by" type="link" token="tokSplit" searchWhenChanged="true">
        <label></label>
        <choice value="sourcetype">Sourcetype</choice>
        <choice value="component">Component</choice>
        <choice value="log_level">Log Level</choice>
        <default>log_level</default>
      </input>
      <html>
        <style>
          /* This Layout Panel Bottom Padding removed to merge Link Input with horizontal line */
          #panel_layout .fieldset{
            padding: 10px 12px 0px 12px !important;          
          }
          /* Increase width of Link input to have options in Single Line */
          #input_link_split_by.input-link{
            width: 720px !important;
          }
          /* Change from flex to -webkit-box for side by side layout */
          #input_radio_split_by.input-link div[data-component="splunk-core:/splunkjs/mvc/components/LinkList"]{
            display: -webkit-box !important;
          }
          /* Create Button Border to make them appear as Tabs */
          #input_link_split_by.input-link button{
            width: 120px !important;
            border-top-color: rgb(255, 255, 255);
            border-top-style: solid;
            border-top-width: 1px;
            border-right-color: rgb(255, 255, 255);
            border-right-style: solid;
            border-right-width: 1px;
            border-left-color: rgb(255, 255, 255);
            border-left-style: solid;
            border-left-width: 1px;
            border-top-left-radius: 10px;
            border-top-right-radius: 10px;
          }
          /* Hide link input bottom message section to merge with Horizontal line */
          .dashboard-panel #input_link_split_by label,
          #input_link_split_by .splunk-choice-input-message{
            display: none !important;
          }
          /* Remove padding from horizontal line */
          #panel_layout .panel-body.html{
            padding: 0px !important;
          }
        </style>
        <hr style="height:1px;border-width:0;color: black;background-color: white;margin: 0px;"/>
      </html>
    </panel>
  </row>
  <row>
    <panel id="panel_single_snapshot">
      <html depends="$alwaysHideCSSOverrideForDashboard$">
        <style>
          #panel_chart_line_error_trend{
            width: 70% !important;
          }
          #panel_chart_pie_error_split{
            width: 30% !important;
          }
          /* Font Size Color Adjustment for Series Compare in Chart Legend */
          div#chart_line_error_trending svg g.highcharts-legend g.highcharts-legend-item text tspan:nth-child(2){
             font-size: 120% !important;
             fill: cyan !important;
             font-weight: bold !important;
          }
          /* Trellis Layout CSS */
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_INFO rect{
            fill: #006D9C !important;
          }
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_WARN rect{
            fill: #F8BE34 !important;
          }
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_WARNING rect{
            fill: #F8BE34 !important;
          }
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_ERROR rect{
            fill: #DC4E41 !important;
          }
        </style>
      </html>
      <single id="single_snapshot">
        <search base="bAllErrorData">
          <query>
| stats count by $tokSplit|s$</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">none</option>
        <option name="height">275</option>
        <option name="rangeColors">["0x53a051","0x0877a6","0xf8be34","0xf1813f","0xdc4e41"]</option>
        <option name="trellis.enabled">1</option>
        <option name="trellis.size">small</option>
        <option name="useColors">1</option>
      </single>
    </panel>
  </row>
  <row>
    <panel id="panel_chart_line_error_trend">
      <chart id="chart_line_error_trending">
        <search base="bAllErrorData">
          <query>
| timechart count by $tokSplit|s$</query>
        </search>
        <option name="charting.fieldColors">{"INFO":"#006D9C","WARN":"#F8BE34","WARNING":"#F8BE34","ERROR":"#DC4E41"}</option>
        <option name="charting.axisLabelsX.majorLabelStyle.overflowMode">ellipsisNone</option>
        <option name="charting.axisLabelsX.majorLabelStyle.rotation">0</option>
        <option name="charting.axisTitleX.visibility">collapsed</option>
        <option name="charting.axisTitleY.visibility">visible</option>
        <option name="charting.axisTitleY2.visibility">visible</option>
        <option name="charting.axisX.abbreviation">none</option>
        <option name="charting.axisX.scale">linear</option>
        <option name="charting.axisY.abbreviation">none</option>
        <option name="charting.axisY.scale">linear</option>
        <option name="charting.axisY2.abbreviation">none</option>
        <option name="charting.axisY2.enabled">0</option>
        <option name="charting.axisY2.scale">inherit</option>
        <option name="charting.chart">area</option>
        <option name="charting.chart.bubbleMaximumSize">50</option>
        <option name="charting.chart.bubbleMinimumSize">10</option>
        <option name="charting.chart.bubbleSizeBy">area</option>
        <option name="charting.chart.nullValueMode">zero</option>
        <option name="charting.chart.showDataLabels">none</option>
        <option name="charting.chart.sliceCollapsingThreshold">0.01</option>
        <option name="charting.chart.stackMode">stacked</option>
        <option name="charting.chart.style">shiny</option>
        <option name="charting.drilldown">none</option>
        <option name="charting.layout.splitSeries">0</option>
        <option name="charting.layout.splitSeries.allowIndependentYRanges">0</option>
        <option name="charting.legend.labelStyle.overflowMode">ellipsisMiddle</option>
        <option name="charting.legend.mode">seriesCompare</option>
        <option name="charting.legend.placement">right</option>
        <option name="charting.lineWidth">2</option>
        <option name="trellis.enabled">0</option>
        <option name="trellis.scales.shared">1</option>
        <option name="trellis.size">medium</option>
      </chart>
    </panel>
    <panel id="panel_chart_pie_error_split">
      <chart id="chart_pie_errors">
        <search base="bAllErrorData">
          <query>
| stats count by $tokSplit|s$</query>
        </search>
        <option name="charting.axisLabelsX.majorLabelStyle.overflowMode">ellipsisNone</option>
        <option name="charting.axisLabelsX.majorLabelStyle.rotation">0</option>
        <option name="charting.axisTitleX.visibility">collapsed</option>
        <option name="charting.axisTitleY.visibility">visible</option>
        <option name="charting.axisTitleY2.visibility">visible</option>
        <option name="charting.axisX.abbreviation">none</option>
        <option name="charting.axisX.scale">linear</option>
        <option name="charting.axisY.abbreviation">none</option>
        <option name="charting.axisY.scale">linear</option>
        <option name="charting.axisY2.abbreviation">none</option>
        <option name="charting.axisY2.enabled">0</option>
        <option name="charting.axisY2.scale">inherit</option>
        <option name="charting.chart">pie</option>
        <option name="charting.chart.bubbleMaximumSize">50</option>
        <option name="charting.chart.bubbleMinimumSize">10</option>
        <option name="charting.chart.bubbleSizeBy">area</option>
        <option name="charting.chart.nullValueMode">zero</option>
        <option name="charting.chart.showDataLabels">none</option>
        <option name="charting.chart.sliceCollapsingThreshold">0.01</option>
        <option name="charting.chart.stackMode">stacked</option>
        <option name="charting.chart.style">shiny</option>
        <option name="charting.drilldown">none</option>
        <option name="charting.fieldColors">{"INFO":"#006D9C","WARN":"#F8BE34","WARNING":"#F8BE34","ERROR":"#DC4E41"}</option>
        <option name="charting.layout.splitSeries">0</option>
        <option name="charting.layout.splitSeries.allowIndependentYRanges">0</option>
        <option name="charting.legend.labelStyle.overflowMode">ellipsisMiddle</option>
        <option name="charting.legend.mode">seriesCompare</option>
        <option name="charting.legend.placement">right</option>
        <option name="charting.lineWidth">2</option>
        <option name="refresh.display">progressbar</option>
        <option name="trellis.enabled">0</option>
        <option name="trellis.scales.shared">1</option>
        <option name="trellis.size">medium</option>
      </chart>
    </panel>
  </row>
</form>
____________________________________________
| makeresults | eval message= "Happy Splunking!!!"

View solution in original post

maayan
Path Finder

Hi, great job it looks good. However, when i click on different tabs, the presented charts don't change. Can you post an example so if I click on tab : 'compenent', I would see a different chart compared to clicking on tab:source type? 
In addition, i do not really understand, can you explain please what is 'tokSplit'? and $tokSplit|s$? and how to define them?
thanks a lot!

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@maayan 

Yes, when you click on the tab, it will execute the search. The token 'tokSplit'  which is set on click of the tab is used in search which makes it execute. So when you change the tab, tokSplit token will be changed and the search with tokSplit token will execute.  If your charts don't change means you need to define a tab-specific token that will be used in the search. 

like, if tab = sourcetype then set tkn_sourcetype with " " values else unset tkn_sourcetype. do it for all tabs.

and just put tkn_sourcetype token in your chart search.

like, YOUR_SEARCH $tkn_sourcetype$

search login won't be impacted as tkn_sourcetype token value is " " (single space).

I hope this will help you.

Thanks
KV
If any of my replies help you to solve the problem Or gain knowledge, an upvote would be appreciated.

0 Karma

maayan
Path Finder

Hi,

Thanks for your reply. I need to create a dashboard with tabs (tab for different topic) without connection to tokens (and without JS script). I also need that because my dashboard performance is too slow, so maybe tabs will help.
 I will try to set the token to "" (empty value) as you said.
can you write me an example please of where to set the token to:"" ?

and do you have an example maybe from a different post to my need? because now i need to add $toekn$ to my search when i don't need that.

thanks a lot

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@maayan 

set " " (single space) instead of "" (empty). I don't have this example handy but you ask if you need help somewhere.  Here I also suggest posting new questions to start a fresh thread.

KV 

0 Karma

maayan
Path Finder
0 Karma

niketn
Legend

Following is a run anywhere example based on Splunk's _internal index.

Step 1. Create a panel with Link Input as per the requirement of tabs. In above example Sourcetype, Component, Log Level are three tabs.
Step 2. Set the required token from Link input in SPL or through <change> event handler of the Link Input to change SPL or hide/show panel using depends respectively. For simplicity, this example changes underlying SPL, there are several examples of depends/rejects on Splunk Docs, Blogs and Splunk Answers.
Step 3. Add <html> panel with horizontal line <hr> to mark border of Link input.
Step 4. Apply CSS override (can be through dashboard <style> section for testing/simplicity or prebuilt-panel, or static CSS file)

PS: CSS applied as per 7.1.x + and may also need some tweaking for Dark Theme vs Light Theme (white Border is used in the Dark Theme example).

alt text

Following is the run anywhere dashboard example using Simple XML and CSS (NO JS REQUIRED). Please try out and let me know any feedback 🙂

<form theme="dark">
  <label>Tabs using Links</label>
  <!-- Base Search to Pull Error Data -->
  <search id="bAllErrorData">
    <query>index=_internal 
| bin _time span=1h
| stats count by _time sourcetype component date_hour log_level
    </query>
    <earliest>$tokTime.earliest$</earliest>
    <latest>$tokTime.latest$</latest>
  </search>
  <fieldset submitButton="false">
    <input id="input_time" type="time" token="tokTime">
      <label></label>
      <default>
        <earliest>-24h@h</earliest>
        <latest>now</latest>
      </default>
    </input>
  </fieldset>
  <row>
    <panel id="panel_layout">
      <input id="input_link_split_by" type="link" token="tokSplit" searchWhenChanged="true">
        <label></label>
        <choice value="sourcetype">Sourcetype</choice>
        <choice value="component">Component</choice>
        <choice value="log_level">Log Level</choice>
        <default>log_level</default>
      </input>
      <html>
        <style>
          /* This Layout Panel Bottom Padding removed to merge Link Input with horizontal line */
          #panel_layout .fieldset{
            padding: 10px 12px 0px 12px !important;          
          }
          /* Increase width of Link input to have options in Single Line */
          #input_link_split_by.input-link{
            width: 720px !important;
          }
          /* Change from flex to -webkit-box for side by side layout */
          #input_radio_split_by.input-link div[data-component="splunk-core:/splunkjs/mvc/components/LinkList"]{
            display: -webkit-box !important;
          }
          /* Create Button Border to make them appear as Tabs */
          #input_link_split_by.input-link button{
            width: 120px !important;
            border-top-color: rgb(255, 255, 255);
            border-top-style: solid;
            border-top-width: 1px;
            border-right-color: rgb(255, 255, 255);
            border-right-style: solid;
            border-right-width: 1px;
            border-left-color: rgb(255, 255, 255);
            border-left-style: solid;
            border-left-width: 1px;
            border-top-left-radius: 10px;
            border-top-right-radius: 10px;
          }
          /* Hide link input bottom message section to merge with Horizontal line */
          .dashboard-panel #input_link_split_by label,
          #input_link_split_by .splunk-choice-input-message{
            display: none !important;
          }
          /* Remove padding from horizontal line */
          #panel_layout .panel-body.html{
            padding: 0px !important;
          }
        </style>
        <hr style="height:1px;border-width:0;color: black;background-color: white;margin: 0px;"/>
      </html>
    </panel>
  </row>
  <row>
    <panel id="panel_single_snapshot">
      <html depends="$alwaysHideCSSOverrideForDashboard$">
        <style>
          #panel_chart_line_error_trend{
            width: 70% !important;
          }
          #panel_chart_pie_error_split{
            width: 30% !important;
          }
          /* Font Size Color Adjustment for Series Compare in Chart Legend */
          div#chart_line_error_trending svg g.highcharts-legend g.highcharts-legend-item text tspan:nth-child(2){
             font-size: 120% !important;
             fill: cyan !important;
             font-weight: bold !important;
          }
          /* Trellis Layout CSS */
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_INFO rect{
            fill: #006D9C !important;
          }
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_WARN rect{
            fill: #F8BE34 !important;
          }
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_WARNING rect{
            fill: #F8BE34 !important;
          }
          #single_snapshot #facet-viz_groupby_field_log_level_groupby_value_ERROR rect{
            fill: #DC4E41 !important;
          }
        </style>
      </html>
      <single id="single_snapshot">
        <search base="bAllErrorData">
          <query>
| stats count by $tokSplit|s$</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">none</option>
        <option name="height">275</option>
        <option name="rangeColors">["0x53a051","0x0877a6","0xf8be34","0xf1813f","0xdc4e41"]</option>
        <option name="trellis.enabled">1</option>
        <option name="trellis.size">small</option>
        <option name="useColors">1</option>
      </single>
    </panel>
  </row>
  <row>
    <panel id="panel_chart_line_error_trend">
      <chart id="chart_line_error_trending">
        <search base="bAllErrorData">
          <query>
| timechart count by $tokSplit|s$</query>
        </search>
        <option name="charting.fieldColors">{"INFO":"#006D9C","WARN":"#F8BE34","WARNING":"#F8BE34","ERROR":"#DC4E41"}</option>
        <option name="charting.axisLabelsX.majorLabelStyle.overflowMode">ellipsisNone</option>
        <option name="charting.axisLabelsX.majorLabelStyle.rotation">0</option>
        <option name="charting.axisTitleX.visibility">collapsed</option>
        <option name="charting.axisTitleY.visibility">visible</option>
        <option name="charting.axisTitleY2.visibility">visible</option>
        <option name="charting.axisX.abbreviation">none</option>
        <option name="charting.axisX.scale">linear</option>
        <option name="charting.axisY.abbreviation">none</option>
        <option name="charting.axisY.scale">linear</option>
        <option name="charting.axisY2.abbreviation">none</option>
        <option name="charting.axisY2.enabled">0</option>
        <option name="charting.axisY2.scale">inherit</option>
        <option name="charting.chart">area</option>
        <option name="charting.chart.bubbleMaximumSize">50</option>
        <option name="charting.chart.bubbleMinimumSize">10</option>
        <option name="charting.chart.bubbleSizeBy">area</option>
        <option name="charting.chart.nullValueMode">zero</option>
        <option name="charting.chart.showDataLabels">none</option>
        <option name="charting.chart.sliceCollapsingThreshold">0.01</option>
        <option name="charting.chart.stackMode">stacked</option>
        <option name="charting.chart.style">shiny</option>
        <option name="charting.drilldown">none</option>
        <option name="charting.layout.splitSeries">0</option>
        <option name="charting.layout.splitSeries.allowIndependentYRanges">0</option>
        <option name="charting.legend.labelStyle.overflowMode">ellipsisMiddle</option>
        <option name="charting.legend.mode">seriesCompare</option>
        <option name="charting.legend.placement">right</option>
        <option name="charting.lineWidth">2</option>
        <option name="trellis.enabled">0</option>
        <option name="trellis.scales.shared">1</option>
        <option name="trellis.size">medium</option>
      </chart>
    </panel>
    <panel id="panel_chart_pie_error_split">
      <chart id="chart_pie_errors">
        <search base="bAllErrorData">
          <query>
| stats count by $tokSplit|s$</query>
        </search>
        <option name="charting.axisLabelsX.majorLabelStyle.overflowMode">ellipsisNone</option>
        <option name="charting.axisLabelsX.majorLabelStyle.rotation">0</option>
        <option name="charting.axisTitleX.visibility">collapsed</option>
        <option name="charting.axisTitleY.visibility">visible</option>
        <option name="charting.axisTitleY2.visibility">visible</option>
        <option name="charting.axisX.abbreviation">none</option>
        <option name="charting.axisX.scale">linear</option>
        <option name="charting.axisY.abbreviation">none</option>
        <option name="charting.axisY.scale">linear</option>
        <option name="charting.axisY2.abbreviation">none</option>
        <option name="charting.axisY2.enabled">0</option>
        <option name="charting.axisY2.scale">inherit</option>
        <option name="charting.chart">pie</option>
        <option name="charting.chart.bubbleMaximumSize">50</option>
        <option name="charting.chart.bubbleMinimumSize">10</option>
        <option name="charting.chart.bubbleSizeBy">area</option>
        <option name="charting.chart.nullValueMode">zero</option>
        <option name="charting.chart.showDataLabels">none</option>
        <option name="charting.chart.sliceCollapsingThreshold">0.01</option>
        <option name="charting.chart.stackMode">stacked</option>
        <option name="charting.chart.style">shiny</option>
        <option name="charting.drilldown">none</option>
        <option name="charting.fieldColors">{"INFO":"#006D9C","WARN":"#F8BE34","WARNING":"#F8BE34","ERROR":"#DC4E41"}</option>
        <option name="charting.layout.splitSeries">0</option>
        <option name="charting.layout.splitSeries.allowIndependentYRanges">0</option>
        <option name="charting.legend.labelStyle.overflowMode">ellipsisMiddle</option>
        <option name="charting.legend.mode">seriesCompare</option>
        <option name="charting.legend.placement">right</option>
        <option name="charting.lineWidth">2</option>
        <option name="refresh.display">progressbar</option>
        <option name="trellis.enabled">0</option>
        <option name="trellis.scales.shared">1</option>
        <option name="trellis.size">medium</option>
      </chart>
    </panel>
  </row>
</form>
____________________________________________
| makeresults | eval message= "Happy Splunking!!!"

nabeel652
Builder

Great solution. Chipping in my 10 cents:

#input_link_split_by.input-link button[aria-checked="true"] {
background-color: green !important;
}

If you want to keep highlighted the last selection on the tabs to make it more visible 

 

 

0 Karma

jacobandrews1
Observer

Do this solution provide load-on-click tab functionality?
Or is this purely cosmetic?

Thanks.

@niketn 

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@jacobandrews1 

Yes, when you click on tab. it will execute the search. The token 'tokSplit'  which is set on click of tab is used in search which makes it execute. So if you are taking reference of this dashboard just take care of tokens. 

 

 

Thanks
KV
▄︻̷̿┻̿═━一

If any of my reply helps you to solve the problem Or gain knowledge, an upvote would be appreciated.

bowesmana
SplunkTrust
SplunkTrust

This is a great post @niketn 

The power of using in-panel CSS to manipulate the layout is amazing. 

Thanks for this really useful post.

 

0 Karma

niketn
Legend

Thanks @bowesmana one of my recently published examples app for our upcoming Splunk .Conf20 session Itsy Bitsy App for Splunk shows two CSS overrides (on similar lines to create Tabs using Dashboard Names  Navigation menu and Links within dashboard. Do check out the SplunkTrust track talk TRU1276C: Splunk dashboard journey: Past, Present and Future with amazing @cmerriman 

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"

kurdbahr
Path Finder

Thanks! Great job!

Get Updates on the Splunk Community!

How to Monitor Google Kubernetes Engine (GKE)

We’ve looked at how to integrate Kubernetes environments with Splunk Observability Cloud, but what about ...

Index This | How can you make 45 using only 4?

October 2024 Edition Hayyy Splunk Education Enthusiasts and the Eternally Curious!  We’re back with this ...

Splunk Education Goes to Washington | Splunk GovSummit 2024

If you’re in the Washington, D.C. area, this is your opportunity to take your career and Splunk skills to the ...