<commit_before>import './styles.scss';
import {View} from 'backbone.marionette';
import hotkeys from '../../utils/hotkeys';
import template from './TreeView.hbs';
import {behavior, className, on} from '../../decorators';
import router from '../../router';
import getComparator from '../../data/tree/comparator';
import {byStatuses, byText, mix} from '../../data/tree/filter';
import {SEARCH_QUERY_KEY} from '../node-search/NodeSearchView';

@className('tree')
@behavior('TooltipBehavior', {position: 'bottom'})
class TreeView extends View {
    template = template;

    cachedQuery = '';
    initialize({routeState, state, tabName, baseUrl, settings}) {
        this.state = state;
        this.routeState = routeState;
        this.baseUrl = baseUrl;
        this.tabName = tabName;
        this.setState();
        this.listenTo(this.routeState, 'change:treeNode', this.selectNode);
        this.listenTo(this.routeState, 'change:testResultTab', this.render);

        this.settings = settings;
        this.listenTo(this.settings, 'change', this.render);
        this.listenTo(this.state, 'change', this.handleStateChange);

        this.listenTo(hotkeys, 'key:up', this.onKeyUp, this);
        this.listenTo(hotkeys, 'key:down', this.onKeyDown, this);
        this.listenTo(hotkeys, 'key:esc', this.onKeyBack, this);
        this.listenTo(hotkeys, 'key:left', this.onKeyBack, this);
    }

    applyFilters() {
        const visibleStatuses = this.settings.getVisibleStatuses();
        const searchQuery = this.state.get(SEARCH_QUERY_KEY);
        const filter = mix(byText(searchQuery), byStatuses(visibleStatuses));

        const sortSettings = this.settings.getTreeSorting();
        const sorter = getComparator(sortSettings);

        this.collection.applyFilterAndSorting(filter, sorter);
    }

    setState() {
        const treeNode = this.routeState.get('treeNode');
        if (treeNode && treeNode.testResult) {
            const uid = treeNode.testResult;
            this.state.set(uid, true);
        }
        if (treeNode && treeNode.testGroup) {
            const uid = treeNode.testGroup;
            this.state.set(uid, true);
        }
    }

    onBeforeRender() {
        this.applyFilters();
    }

    handleStateChange() {
        const query = this.state.get(SEARCH_QUERY_KEY);
        // need to check this ot to re-render nodes on folding
        if (query !== this.cachedQuery) {
            this.cachedQuery = query;
            this.render();
        }
    }

    onRender() {
        this.selectNode();
        if (this.state.get(SEARCH_QUERY_KEY)) {
            this.$('.node__title').each((i, node) => {
                this.$(node).parent().addClass('node__expanded');
            });
        } else {
            this.restoreState();
        }
    }

    selectNode() {
        const previous = this.routeState.previous('treeNode');
        this.toggleNode(previous, false);
        const current = this.routeState.get('treeNode');
        this.toggleNode(current, true);
        this.restoreState();
    }

    toggleNode(node, active = true) {
        if (node) {
            const el = this.findElement(node);
            el.toggleClass('node__title_active', active);
            this.changeState(node.testResult);
            this.changeState(node.testGroup);
        }
    }

    changeState(uid, active = true) {
        if (active) {
            this.state.set(uid, true);
        } else {
            this.state.unset(uid);
        }
    }

    restoreState() {
        this.$('[data-uid]').each((i, node) => {
            const el = this.$(node);
            const uid = el.data('uid');
            el.toggleClass('node__expanded', this.state.has(uid));
        });
        this.$('.node__title_active').parents('.node').toggleClass('node__expanded', true);
        this.$('.node__expanded').parents('.node').toggleClass('node__expanded', true);
    }

    findElement(treeNode) {
        if (treeNode.testResult) {
            return this.$(`[data-uid='${treeNode.testResult}'][data-parentUid='${treeNode.testGroup}']`);
        } else {<commit_msg>I have fixed "If user click on a test items, other previously opened test packages also got expanded." #575<commit_after>import './styles.scss';
import {View} from 'backbone.marionette';
import hotkeys from '../../utils/hotkeys';
import template from './TreeView.hbs';
import {behavior, className, on} from '../../decorators';
import router from '../../router';
import getComparator from '../../data/tree/comparator';
import {byStatuses, byText, mix} from '../../data/tree/filter';
import {SEARCH_QUERY_KEY} from '../node-search/NodeSearchView';

@className('tree')
@behavior('TooltipBehavior', {position: 'bottom'})
class TreeView extends View {
    template = template;

    cachedQuery = '';
    initialize({routeState, state, tabName, baseUrl, settings}) {
        this.state = state;
        this.routeState = routeState;
        this.baseUrl = baseUrl;
        this.tabName = tabName;
        this.setState();
        this.listenTo(this.routeState, 'change:treeNode', this.selectNode);
        this.listenTo(this.routeState, 'change:testResultTab', this.render);

        this.settings = settings;
        this.listenTo(this.settings, 'change', this.render);
        this.listenTo(this.state, 'change', this.handleStateChange);

        this.listenTo(hotkeys, 'key:up', this.onKeyUp, this);
        this.listenTo(hotkeys, 'key:down', this.onKeyDown, this);
        this.listenTo(hotkeys, 'key:esc', this.onKeyBack, this);
        this.listenTo(hotkeys, 'key:left', this.onKeyBack, this);
    }

    applyFilters() {
        const visibleStatuses = this.settings.getVisibleStatuses();
        const searchQuery = this.state.get(SEARCH_QUERY_KEY);
        const filter = mix(byText(searchQuery), byStatuses(visibleStatuses));

        const sortSettings = this.settings.getTreeSorting();
        const sorter = getComparator(sortSettings);

        this.collection.applyFilterAndSorting(filter, sorter);
    }

    setState() {
        const treeNode = this.routeState.get('treeNode');
        if (treeNode && treeNode.testResult) {
            const uid = treeNode.testResult;
            this.state.set(uid, true);
        }
        if (treeNode && treeNode.testGroup) {
            const uid = treeNode.testGroup;
            this.state.set(uid, true);
        }
    }

    onBeforeRender() {
        this.applyFilters();
    }

    handleStateChange() {
        const query = this.state.get(SEARCH_QUERY_KEY);
        // need to check this ot to re-render nodes on folding
        if (query !== this.cachedQuery) {
            this.cachedQuery = query;
            this.render();
        }
    }

    onRender() {
        this.selectNode();
        if (this.state.get(SEARCH_QUERY_KEY)) {
            this.$('.node__title').each((i, node) => {
                this.$(node).parent().addClass('node__expanded');
            });
        } else {
            this.restoreState();
        }
    }

    selectNode() {
        const previous = this.routeState.previous('treeNode');
        this.toggleNode(previous, false);
        const current = this.routeState.get('treeNode');
        this.toggleNode(current, true);
        this.restoreState();
    }

    toggleNode(node, active = true) {
        if (node) {
            const el = this.findElement(node);
            el.toggleClass('node__title_active', active);
            this.changeState(node.testResult);
            this.changeState(node.testGroup);
        }
    }

    changeState(uid, active = true) {
        if (active) {
            this.state.set(uid, true);
        } else {
            this.state.unset(uid);
        }
    }

    restoreState() {
        this.$('[data-uid]').each((i, node) => {
            const el = this.$(node);
            const uid = el.data('uid');
            el.toggleClass('node__expanded', this.state.has(uid));
        });
        this.$('.node__title_active').parents('.node').toggleClass('node__expanded', true);
    }

    findElement(treeNode) {
        if (treeNode.testResult) {
            return this.$(`[data-uid='${treeNode.testResult}'][data-parentUid='${treeNode.testGroup}']`);
        } else {
{'fraction_non_alphanumeric': 0.14771066731612276, 'fraction_numerical': 0.0003653190452995616, 'mean_word_length': 2.4292275574112736, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3402017', 'n_tokens_mistral': 2407, 'n_tokens_neox': 2396, 'n_words': 450}