Dashboards & Visualizations

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

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

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

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!!!"

jacobandrews1
Observer

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

Thanks.

@niketn-deceased 

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.

0 Karma

bowesmana
SplunkTrust
SplunkTrust

This is a great post @niketn-deceased 

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

Thanks for this really useful post.

 

0 Karma

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!

Splunk Forwarders and Forced Time Based Load Balancing

Splunk customers use universal forwarders to collect and send data to Splunk. A universal forwarder can send ...

NEW! Log Views in Splunk Observability Dashboards Gives Context From a Single Page

Today, Splunk Observability releases log views, a new feature for users to add their logs data from Splunk Log ...

Last Chance to Submit Your Paper For BSides Splunk - Deadline is August 12th!

Hello everyone! Don't wait to submit - The deadline is August 12th! We have truly missed the community so ...